From b43660ceb465cc1015ea2c90ffeec722f064f7ee Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Mon, 1 Jun 2026 16:35:06 -0700 Subject: [PATCH 1/4] go.mod: bump top-level cluster-api to v1.12.8 Bump sigs.k8s.io/cluster-api from v1.11.8 to v1.12.8 to pick up the graduated v1beta2 API, which includes MachineTaint support for setting node taints via MachineSet/MachineDeployment. --- go.mod | 8 ++++---- go.sum | 54 ++++++++++++++++++++++++++++++------------------------ 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index 0f8eb6ed6e..2dac635a0e 100644 --- a/go.mod +++ b/go.mod @@ -123,14 +123,14 @@ require ( k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 libvirt.org/go/libvirtxml v1.10002.0 - sigs.k8s.io/cluster-api v1.11.8 + sigs.k8s.io/cluster-api v1.12.8 sigs.k8s.io/cluster-api-provider-aws/v2 v2.10.1-0.20260226000023-67627b2da0df sigs.k8s.io/cluster-api-provider-azure v1.21.1-0.20250929163617-2c4eaa611a39 sigs.k8s.io/cluster-api-provider-gcp v1.10.1-0.20251030112604-5c2bfd5aadb3 sigs.k8s.io/cluster-api-provider-ibmcloud v0.12.2 sigs.k8s.io/cluster-api-provider-openstack v0.13.0 sigs.k8s.io/cluster-api-provider-vsphere v1.14.0 - sigs.k8s.io/controller-runtime v0.22.4 + sigs.k8s.io/controller-runtime v0.22.5 sigs.k8s.io/controller-tools v0.19.0 sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 sigs.k8s.io/yaml v1.6.0 @@ -322,7 +322,7 @@ require ( go.opentelemetry.io/otel/metric v1.40.0 // indirect go.opentelemetry.io/otel/trace v1.40.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/mod v0.32.0 golang.org/x/net v0.49.0 // indirect @@ -340,7 +340,7 @@ require ( gorm.io/gorm v1.24.5 // indirect k8s.io/apiserver v0.35.1 // indirect k8s.io/cli-runtime v0.35.1 // indirect - k8s.io/cluster-bootstrap v0.33.3 // indirect + k8s.io/cluster-bootstrap v0.34.2 // indirect k8s.io/component-base v0.35.1 // indirect k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b // indirect k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect diff --git a/go.sum b/go.sum index 9fc701f02a..87e605453d 100644 --- a/go.sum +++ b/go.sum @@ -312,8 +312,8 @@ github.com/containers/storage v1.54.0 h1:xwYAlf6n9OnIlURQLLg3FYHbO74fQ/2W2N6EtQE github.com/containers/storage v1.54.0/go.mod h1:PlMOoinRrBSnhYODLxt4EXl0nmJt+X0kjG0Xdt9fMTw= github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= -github.com/coredns/corefile-migration v1.0.30 h1:ljZNPGgna+4yKv81gfkvkgLEWdtz0NjBR1glaiPI140= -github.com/coredns/corefile-migration v1.0.30/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= +github.com/coredns/corefile-migration v1.0.32 h1:tlbtXBpt7UzmedEoMqnfqOTnGCvzYfJ/Rrfqf+/W+TY= +github.com/coredns/corefile-migration v1.0.32/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= github.com/coreos/go-json v0.0.0-20230131223807-18775e0fb4fb h1:rmqyI19j3Z/74bIRhuC59RB442rXUazKNueVpfJPxg4= github.com/coreos/go-json v0.0.0-20230131223807-18775e0fb4fb/go.mod h1:rcFZM3uxVvdyNmsAV2jopgPD1cs5SPWJWU5dOz2LUnw= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= @@ -587,8 +587,8 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ= -github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -862,8 +862,14 @@ github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= +github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= +github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.1.1 h1:9Dfeed5/Mgaxb9lHRAftLK9pVfYETvHn+If6lywVhJc= +github.com/olekukonko/ll v0.1.1/go.mod h1:2dJo+hYZcJMLMbKwHEWvxCUbAOLc/CXWS9noET22Mdo= +github.com/olekukonko/tablewriter v1.0.9 h1:XGwRsYLC2bY7bNd93Dk51bcPZksWZmLYuaTHR0FqfL8= +github.com/olekukonko/tablewriter v1.0.9/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -935,8 +941,8 @@ github.com/ovirt/go-ovirt v0.0.0-20210809163552-d4276e35d3db/go.mod h1:Zkdj9/rW6 github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= @@ -996,8 +1002,8 @@ github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE= github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= -github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= -github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0= @@ -1029,15 +1035,15 @@ github.com/sivchari/tenv v1.12.1 h1:+E0QzjktdnExv/wwsnnyk4oqZBUfuh89YMQT1cyuvSY= github.com/sivchari/tenv v1.12.1/go.mod h1:1LjSOUCc25snIr5n3DtGGrENhX3LuWefcplwVGC24mw= github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM= github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= -github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= -github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= @@ -1046,8 +1052,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= -github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= @@ -1177,8 +1183,8 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= 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/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -1442,8 +1448,8 @@ k8s.io/cli-runtime v0.35.1 h1:uKcXFe8J7AMAM4Gm2JDK4mp198dBEq2nyeYtO+JfGJE= k8s.io/cli-runtime v0.35.1/go.mod h1:55/hiXIq1C8qIJ3WBrWxEwDLdHQYhBNRdZOz9f7yvTw= k8s.io/client-go v0.35.1 h1:+eSfZHwuo/I19PaSxqumjqZ9l5XiTEKbIaJ+j1wLcLM= k8s.io/client-go v0.35.1/go.mod h1:1p1KxDt3a0ruRfc/pG4qT/3oHmUj1AhSHEcxNSGg+OA= -k8s.io/cluster-bootstrap v0.33.3 h1:u2NTxJ5CFSBFXaDxLQoOWMly8eni31psVso+caq6uwI= -k8s.io/cluster-bootstrap v0.33.3/go.mod h1:p970f8u8jf273zyQ5raD8WUu2XyAl0SAWOY82o7i/ds= +k8s.io/cluster-bootstrap v0.34.2 h1:oKckPeunVCns37BntcsxaOesDul32yzGd3DFLjW2fc8= +k8s.io/cluster-bootstrap v0.34.2/go.mod h1:f21byPR7X5nt12ivZi+J3pb4sG4SH6VySX8KAAJA8BY= k8s.io/code-generator v0.23.3/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk= k8s.io/code-generator v0.35.1 h1:yLKR2la7Z9cWT5qmk67ayx8xXLM4RRKQMnC8YPvTWRI= k8s.io/code-generator v0.35.1/go.mod h1:F2Fhm7aA69tC/VkMXLDokdovltXEF026Tb9yfQXQWKg= @@ -1479,8 +1485,8 @@ mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8 mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0 h1:XotDXzqvJ8Nx5eiZZueLpTuafJz8SiodgOemI+w87QU= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/cluster-api v1.11.8 h1:AY6LAJITs7foZEyFljtauI6RxS8o/Vb2Ne62gFJt2sc= -sigs.k8s.io/cluster-api v1.11.8/go.mod h1:Xpz9JeFxT3dgP+wUhTs9iSw0u/3XdO8ZzWIYAZvF0VY= +sigs.k8s.io/cluster-api v1.12.8 h1:37SLcQRG9EMhmsZJwyEx8pNBkmY1Xhog53slhDy44m4= +sigs.k8s.io/cluster-api v1.12.8/go.mod h1:Xz6YnayDc2+/3OA1i6wlXrhmErV1KKJmyfs7JzMDn+k= sigs.k8s.io/cluster-api-provider-aws/v2 v2.10.1-0.20260226000023-67627b2da0df h1:jTXXHQqzCj0qRupZC4tWleQ+yL7JSdqT5WzovG1PlBI= sigs.k8s.io/cluster-api-provider-aws/v2 v2.10.1-0.20260226000023-67627b2da0df/go.mod h1:vbVa187QP4/y7/03JqhYBmou4xabCRnuRoHwhE2BgE8= sigs.k8s.io/cluster-api-provider-gcp v1.10.1-0.20251030112604-5c2bfd5aadb3 h1:hX2TCLts4fGf33RAkfMViy5qHZfMLZJfeu2BGYxXQkI= From 85fbba18a3c5f2c6ac208b325a4f8931bba416ff Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Mon, 1 Jun 2026 16:35:31 -0700 Subject: [PATCH 2/4] go.mod: bump controller deps to v1.12.8 --- cluster-api/cluster-api/go.mod | 58 ++++++------- cluster-api/cluster-api/go.sum | 152 ++++++++++++++++----------------- 2 files changed, 105 insertions(+), 105 deletions(-) diff --git a/cluster-api/cluster-api/go.mod b/cluster-api/cluster-api/go.mod index 72a87c58a2..51458cdc99 100644 --- a/cluster-api/cluster-api/go.mod +++ b/cluster-api/cluster-api/go.mod @@ -2,7 +2,7 @@ module openshift/installer/cluster-api/providers/core go 1.24.0 -require sigs.k8s.io/cluster-api v1.11.3 +require sigs.k8s.io/cluster-api v1.12.8 require ( cel.dev/expr v0.25.1 // indirect @@ -47,7 +47,7 @@ require ( github.com/google/gnostic-models v0.7.1 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -72,48 +72,48 @@ require ( github.com/valyala/fastjson v1.6.4 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect - go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/otel/sdk v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect + go.opentelemetry.io/otel v1.40.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect + go.opentelemetry.io/otel/metric v1.40.0 // indirect + go.opentelemetry.io/otel/sdk v1.40.0 // indirect + go.opentelemetry.io/otel/trace v1.40.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.44.0 // indirect + golang.org/x/crypto v0.47.0 // indirect golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/oauth2 v0.33.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/term v0.37.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/term v0.39.0 // indirect + golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.14.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect - google.golang.org/grpc v1.77.0 // indirect - google.golang.org/protobuf v1.36.10 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/grpc v1.79.3 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - k8s.io/api v0.34.2 // indirect - k8s.io/apiextensions-apiserver v0.34.2 // indirect - k8s.io/apimachinery v0.34.2 // indirect - k8s.io/apiserver v0.34.2 // indirect - k8s.io/client-go v0.34.2 // indirect + k8s.io/api v0.34.3 // indirect + k8s.io/apiextensions-apiserver v0.34.3 // indirect + k8s.io/apimachinery v0.34.3 // indirect + k8s.io/apiserver v0.34.3 // indirect + k8s.io/client-go v0.34.3 // indirect k8s.io/cluster-bootstrap v0.34.2 // indirect - k8s.io/component-base v0.34.2 // indirect + k8s.io/component-base v0.34.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect - sigs.k8s.io/controller-runtime v0.22.4 // indirect + sigs.k8s.io/controller-runtime v0.22.5 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/cluster-api/cluster-api/go.sum b/cluster-api/cluster-api/go.sum index 263de33f53..bbcb0a15b5 100644 --- a/cluster-api/cluster-api/go.sum +++ b/cluster-api/cluster-api/go.sum @@ -24,8 +24,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= -github.com/coredns/corefile-migration v1.0.29 h1:g4cPYMXXDDs9uLE2gFYrJaPBuUAR07eEMGyh9JBE13w= -github.com/coredns/corefile-migration v1.0.29/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= +github.com/coredns/corefile-migration v1.0.32 h1:tlbtXBpt7UzmedEoMqnfqOTnGCvzYfJ/Rrfqf+/W+TY= +github.com/coredns/corefile-migration v1.0.32/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= @@ -112,14 +112,14 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= 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/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= 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.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -148,8 +148,8 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/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/onsi/ginkgo/v2 v2.25.1 h1:Fwp6crTREKM+oA6Cz4MsO8RhKQzs2/gOIVOUscMAfZY= -github.com/onsi/ginkgo/v2 v2.25.1/go.mod h1:ppTWQ1dh9KM/F1XgpeRqelR+zHVwV81DGRSDnFxK7Sk= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -197,42 +197,40 @@ 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/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo= -go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk= -go.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0= -go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI= -go.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A= -go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo= +go.etcd.io/etcd/api/v3 v3.6.6 h1:mcaMp3+7JawWv69p6QShYWS8cIWUOl32bFLb6qf8pOQ= +go.etcd.io/etcd/api/v3 v3.6.6/go.mod h1:f/om26iXl2wSkcTA1zGQv8reJRSLVdoEBsi4JdfMrx4= +go.etcd.io/etcd/client/pkg/v3 v3.6.6 h1:uoqgzSOv2H9KlIF5O1Lsd8sW+eMLuV6wzE3q5GJGQNs= +go.etcd.io/etcd/client/pkg/v3 v3.6.6/go.mod h1:YngfUVmvsvOJ2rRgStIyHsKtOt9SZI2aBJrZiWJhCbI= +go.etcd.io/etcd/client/v3 v3.6.6 h1:G5z1wMf5B9SNexoxOHUGBaULurOZPIgGPsW6CN492ec= +go.etcd.io/etcd/client/v3 v3.6.6/go.mod h1:36Qv6baQ07znPR3+n7t+Rk5VHEzVYPvFfGmfF4wBHV8= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= +go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= -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/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -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 v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -240,44 +238,46 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= -golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0= golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= 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.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= 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-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.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= -golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= 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.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= 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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= -golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= 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= @@ -286,14 +286,14 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba h1:B14OtaXuMaCQsl2deSvNkyPKIzq3BjfxQp8d00QyWx4= -google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:G5IanEx8/PgI9w6CFcYQf7jMtHQhZruvfM1i3qOqk5U= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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= @@ -304,20 +304,20 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 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= -k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= -k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= -k8s.io/apiextensions-apiserver v0.34.2 h1:WStKftnGeoKP4AZRz/BaAAEJvYp4mlZGN0UCv+uvsqo= -k8s.io/apiextensions-apiserver v0.34.2/go.mod h1:398CJrsgXF1wytdaanynDpJ67zG4Xq7yj91GrmYN2SE= -k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= -k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/apiserver v0.34.2 h1:2/yu8suwkmES7IzwlehAovo8dDE07cFRC7KMDb1+MAE= -k8s.io/apiserver v0.34.2/go.mod h1:gqJQy2yDOB50R3JUReHSFr+cwJnL8G1dzTA0YLEqAPI= -k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= -k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= +k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= +k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk= +k8s.io/apiextensions-apiserver v0.34.3 h1:p10fGlkDY09eWKOTeUSioxwLukJnm+KuDZdrW71y40g= +k8s.io/apiextensions-apiserver v0.34.3/go.mod h1:aujxvqGFRdb/cmXYfcRTeppN7S2XV/t7WMEc64zB5A0= +k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= +k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.3 h1:uGH1qpDvSiYG4HVFqc6A3L4CKiX+aBWDrrsxHYK0Bdo= +k8s.io/apiserver v0.34.3/go.mod h1:QPnnahMO5C2m3lm6fPW3+JmyQbvHZQ8uudAu/493P2w= +k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= +k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= k8s.io/cluster-bootstrap v0.34.2 h1:oKckPeunVCns37BntcsxaOesDul32yzGd3DFLjW2fc8= k8s.io/cluster-bootstrap v0.34.2/go.mod h1:f21byPR7X5nt12ivZi+J3pb4sG4SH6VySX8KAAJA8BY= -k8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ= -k8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM= +k8s.io/component-base v0.34.3 h1:zsEgw6ELqK0XncCQomgO9DpUIzlrYuZYA0Cgo+JWpVk= +k8s.io/component-base v0.34.3/go.mod h1:5iIlD8wPfWE/xSHTRfbjuvUul2WZbI2nOUK65XL0E/c= 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-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= @@ -326,15 +326,15 @@ k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzk k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/cluster-api v1.11.3 h1:apxfugbP1X8AG7THCM74CTarCOW4H2oOc6hlbm1hY80= -sigs.k8s.io/cluster-api v1.11.3/go.mod h1:CA471SACi81M8DzRKTlWpHV33G0cfWEj7sC4fALFVok= -sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= -sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/cluster-api v1.12.8 h1:37SLcQRG9EMhmsZJwyEx8pNBkmY1Xhog53slhDy44m4= +sigs.k8s.io/cluster-api v1.12.8/go.mod h1:Xz6YnayDc2+/3OA1i6wlXrhmErV1KKJmyfs7JzMDn+k= +sigs.k8s.io/controller-runtime v0.22.5 h1:v3nfSUMowX/2WMp27J9slwGFyAt7IV0YwBxAkrUr0GE= +sigs.k8s.io/controller-runtime v0.22.5/go.mod h1:pc5SoYWnWI6I+cBHYYdZ7B6YHZVY5xNfll88JB+vniI= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 h1:2WOzJpHUBVrrkDjU4KBT8n5LDcj824eX0I5UKcgeRUs= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= From de8d975775210e3289f64e7c1a8d1474cd4c435a Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Mon, 1 Jun 2026 16:35:37 -0700 Subject: [PATCH 3/4] data: regenerate capi core-components.yaml --- data/data/cluster-api/core-components.yaml | 2350 +++++++++++++++++--- 1 file changed, 2064 insertions(+), 286 deletions(-) diff --git a/data/data/cluster-api/core-components.yaml b/data/data/cluster-api/core-components.yaml index b4ac1f5cf8..721ddc8ebe 100644 --- a/data/data/cluster-api/core-components.yaml +++ b/data/data/cluster-api/core-components.yaml @@ -11,7 +11,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: clusterclasses.cluster.x-k8s.io @@ -2990,6 +2990,53 @@ spec: format: int32 minimum: 0 type: integer + unhealthyMachineConditions: + description: |- + unhealthyMachineConditions contains a list of the machine conditions that determine + whether a machine is considered unhealthy. The conditions are combined in a + logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + items: + description: |- + UnhealthyMachineCondition represents a Machine condition type and value with a timeout + specified as a duration. When the named condition has been in the given + status for at least the timeout value, a machine is considered unhealthy. + properties: + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + timeoutSeconds: + description: |- + timeoutSeconds is the duration that a machine must be in a given status for, + after which the machine is considered unhealthy. + For example, with a value of "3600", the machine must match the status + for at least 1 hour before being considered unhealthy. + format: int32 + minimum: 0 + type: integer + type: + description: type of Machine condition + maxLength: 316 + minLength: 1 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + x-kubernetes-validations: + - message: 'type must not be one of: Ready, Available, + HealthCheckSucceeded, OwnerRemediated, ExternallyRemediated' + rule: '!(self in [''Ready'',''Available'',''HealthCheckSucceeded'',''OwnerRemediated'',''ExternallyRemediated''])' + required: + - status + - timeoutSeconds + - type + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-type: atomic unhealthyNodeConditions: description: |- unhealthyNodeConditions contains a list of conditions that determine @@ -3010,7 +3057,7 @@ spec: description: |- timeoutSeconds is the duration that a node must be in a given status for, after which the node is considered unhealthy. - For example, with a value of "1h", the node must match the status + For example, with a value of "3600", the node must match the status for at least 1 hour before being considered unhealthy. format: int32 minimum: 0 @@ -3341,6 +3388,20 @@ spec: required: - templateRef type: object + kubernetesVersions: + description: |- + kubernetesVersions is the list of Kubernetes versions that can be + used for clusters using this ClusterClass. + The list of version must be ordered from the older to the newer version, and there should be + at least one version for every minor in between the first and the last version. + items: + maxLength: 256 + minLength: 1 + type: string + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-type: atomic patches: description: |- patches defines the patches which are applied to customize @@ -3566,6 +3627,24 @@ spec: minItems: 1 type: array x-kubernetes-list-type: atomic + upgrade: + description: upgrade defines the upgrade configuration for clusters + using this ClusterClass. + minProperties: 1 + properties: + external: + description: external defines external runtime extensions for + upgrade operations. + minProperties: 1 + properties: + generateUpgradePlanExtension: + description: generateUpgradePlanExtension references an extension + which is called to generate upgrade plan. + maxLength: 512 + minLength: 1 + type: string + type: object + type: object variables: description: |- variables defines the variables which can be configured @@ -4135,6 +4214,54 @@ spec: format: int32 minimum: 0 type: integer + unhealthyMachineConditions: + description: |- + unhealthyMachineConditions contains a list of the machine conditions that determine + whether a machine is considered unhealthy. The conditions are combined in a + logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + items: + description: |- + UnhealthyMachineCondition represents a Machine condition type and value with a timeout + specified as a duration. When the named condition has been in the given + status for at least the timeout value, a machine is considered unhealthy. + properties: + status: + description: status of the condition, one + of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + timeoutSeconds: + description: |- + timeoutSeconds is the duration that a machine must be in a given status for, + after which the machine is considered unhealthy. + For example, with a value of "3600", the machine must match the status + for at least 1 hour before being considered unhealthy. + format: int32 + minimum: 0 + type: integer + type: + description: type of Machine condition + maxLength: 316 + minLength: 1 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + x-kubernetes-validations: + - message: 'type must not be one of: Ready, + Available, HealthCheckSucceeded, OwnerRemediated, + ExternallyRemediated' + rule: '!(self in [''Ready'',''Available'',''HealthCheckSucceeded'',''OwnerRemediated'',''ExternallyRemediated''])' + required: + - status + - timeoutSeconds + - type + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-type: atomic unhealthyNodeConditions: description: |- unhealthyNodeConditions contains a list of conditions that determine @@ -4155,7 +4282,7 @@ spec: description: |- timeoutSeconds is the duration that a node must be in a given status for, after which the node is considered unhealthy. - For example, with a value of "1h", the node must match the status + For example, with a value of "3600", the node must match the status for at least 1 hour before being considered unhealthy. format: int32 minimum: 0 @@ -5309,7 +5436,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: clusterresourcesetbindings.addons.cluster.x-k8s.io @@ -5743,7 +5870,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: clusterresourcesets.addons.cluster.x-k8s.io @@ -6646,7 +6773,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: clusters.cluster.x-k8s.io @@ -7511,9 +7638,6 @@ spec: description: port is the port on which the API server is serving. format: int32 type: integer - required: - - host - - port type: object controlPlaneRef: description: |- @@ -9190,6 +9314,54 @@ spec: format: int32 minimum: 0 type: integer + unhealthyMachineConditions: + description: |- + unhealthyMachineConditions contains a list of the machine conditions that determine + whether a machine is considered unhealthy. The conditions are combined in a + logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + items: + description: |- + UnhealthyMachineCondition represents a Machine condition type and value with a timeout + specified as a duration. When the named condition has been in the given + status for at least the timeout value, a machine is considered unhealthy. + properties: + status: + description: status of the condition, one of + True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + timeoutSeconds: + description: |- + timeoutSeconds is the duration that a machine must be in a given status for, + after which the machine is considered unhealthy. + For example, with a value of "3600", the machine must match the status + for at least 1 hour before being considered unhealthy. + format: int32 + minimum: 0 + type: integer + type: + description: type of Machine condition + maxLength: 316 + minLength: 1 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + x-kubernetes-validations: + - message: 'type must not be one of: Ready, + Available, HealthCheckSucceeded, OwnerRemediated, + ExternallyRemediated' + rule: '!(self in [''Ready'',''Available'',''HealthCheckSucceeded'',''OwnerRemediated'',''ExternallyRemediated''])' + required: + - status + - timeoutSeconds + - type + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-type: atomic unhealthyNodeConditions: description: |- unhealthyNodeConditions contains a list of conditions that determine @@ -9210,7 +9382,7 @@ spec: description: |- timeoutSeconds is the duration that a node must be in a given status for, after which the node is considered unhealthy. - For example, with a value of "1h", the node must match the status + For example, with a value of "3600", the node must match the status for at least 1 hour before being considered unhealthy. format: int32 minimum: 0 @@ -9591,6 +9763,54 @@ spec: format: int32 minimum: 0 type: integer + unhealthyMachineConditions: + description: |- + unhealthyMachineConditions contains a list of the machine conditions that determine + whether a machine is considered unhealthy. The conditions are combined in a + logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + items: + description: |- + UnhealthyMachineCondition represents a Machine condition type and value with a timeout + specified as a duration. When the named condition has been in the given + status for at least the timeout value, a machine is considered unhealthy. + properties: + status: + description: status of the condition, + one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + timeoutSeconds: + description: |- + timeoutSeconds is the duration that a machine must be in a given status for, + after which the machine is considered unhealthy. + For example, with a value of "3600", the machine must match the status + for at least 1 hour before being considered unhealthy. + format: int32 + minimum: 0 + type: integer + type: + description: type of Machine condition + maxLength: 316 + minLength: 1 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + x-kubernetes-validations: + - message: 'type must not be one of: Ready, + Available, HealthCheckSucceeded, OwnerRemediated, + ExternallyRemediated' + rule: '!(self in [''Ready'',''Available'',''HealthCheckSucceeded'',''OwnerRemediated'',''ExternallyRemediated''])' + required: + - status + - timeoutSeconds + - type + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-type: atomic unhealthyNodeConditions: description: |- unhealthyNodeConditions contains a list of conditions that determine @@ -9611,7 +9831,7 @@ spec: description: |- timeoutSeconds is the duration that a node must be in a given status for, after which the node is considered unhealthy. - For example, with a value of "1h", the node must match the status + For example, with a value of "3600", the node must match the status for at least 1 hour before being considered unhealthy. format: int32 minimum: 0 @@ -10425,7 +10645,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: extensionconfigs.runtime.cluster.x-k8s.io @@ -11168,7 +11388,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: ipaddressclaims.ipam.cluster.x-k8s.io @@ -11787,7 +12007,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: ipaddresses.ipam.cluster.x-k8s.io @@ -12143,7 +12363,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: machinedeployments.cluster.x-k8s.io @@ -14493,6 +14713,77 @@ spec: x-kubernetes-list-map-keys: - conditionType x-kubernetes-list-type: map + taints: + description: |- + taints are the node taints that Cluster API will manage. + This list is not necessarily complete: other Kubernetes components may add or remove other taints from nodes, + e.g. the node controller might add the node.kubernetes.io/not-ready taint. + Only those taints defined in this list will be added or removed by core Cluster API controllers. + + There can be at most 64 taints. + A pod would have to tolerate all existing taints to run on the corresponding node. + + NOTE: This list is implemented as a "map" type, meaning that individual elements can be managed by different owners. + items: + description: MachineTaint defines a taint equivalent to + corev1.Taint, but additionally having a propagation field. + properties: + effect: + description: effect is the effect for the taint. Valid + values are NoSchedule, PreferNoSchedule and NoExecute. + enum: + - NoSchedule + - PreferNoSchedule + - NoExecute + type: string + key: + description: |- + key is the taint key to be applied to a node. + Must be a valid qualified name of maximum size 63 characters + with an optional subdomain prefix of maximum size 253 characters, + separated by a `/`. + maxLength: 317 + minLength: 1 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/)?([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$ + type: string + x-kubernetes-validations: + - message: key must be a valid qualified name of max + size 63 characters with an optional subdomain prefix + of max size 253 characters + rule: 'self.contains(''/'') ? ( self.split(''/'') + [0].size() <= 253 && self.split(''/'') [1].size() + <= 63 && self.split(''/'').size() == 2 ) : self.size() + <= 63' + propagation: + description: |- + propagation defines how this taint should be propagated to nodes. + Valid values are 'Always' and 'OnInitialization'. + Always: The taint will be continuously reconciled. If it is not set for a node, it will be added during reconciliation. + OnInitialization: The taint will be added during node initialization. If it gets removed from the node later on it will not get added again. + enum: + - Always + - OnInitialization + type: string + value: + description: |- + value is the taint value corresponding to the taint key. + It must be a valid label value of maximum size 63 characters. + maxLength: 63 + minLength: 1 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ + type: string + required: + - effect + - key + - propagation + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - key + - effect + x-kubernetes-list-type: map version: description: |- version defines the desired Kubernetes version. @@ -14746,7 +15037,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: machinedrainrules.cluster.x-k8s.io @@ -15535,7 +15826,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: machinehealthchecks.cluster.x-k8s.io @@ -16610,6 +16901,53 @@ spec: format: int32 minimum: 0 type: integer + unhealthyMachineConditions: + description: |- + unhealthyMachineConditions contains a list of the machine conditions that determine + whether a machine is considered unhealthy. The conditions are combined in a + logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + items: + description: |- + UnhealthyMachineCondition represents a Machine condition type and value with a timeout + specified as a duration. When the named condition has been in the given + status for at least the timeout value, a machine is considered unhealthy. + properties: + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + timeoutSeconds: + description: |- + timeoutSeconds is the duration that a machine must be in a given status for, + after which the machine is considered unhealthy. + For example, with a value of "3600", the machine must match the status + for at least 1 hour before being considered unhealthy. + format: int32 + minimum: 0 + type: integer + type: + description: type of Machine condition + maxLength: 316 + minLength: 1 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + x-kubernetes-validations: + - message: 'type must not be one of: Ready, Available, HealthCheckSucceeded, + OwnerRemediated, ExternallyRemediated' + rule: '!(self in [''Ready'',''Available'',''HealthCheckSucceeded'',''OwnerRemediated'',''ExternallyRemediated''])' + required: + - status + - timeoutSeconds + - type + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-type: atomic unhealthyNodeConditions: description: |- unhealthyNodeConditions contains a list of conditions that determine @@ -16630,7 +16968,7 @@ spec: description: |- timeoutSeconds is the duration that a node must be in a given status for, after which the node is considered unhealthy. - For example, with a value of "1h", the node must match the status + For example, with a value of "3600", the node must match the status for at least 1 hour before being considered unhealthy. format: int32 minimum: 0 @@ -16969,7 +17307,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: machinepools.cluster.x-k8s.io @@ -18923,6 +19261,77 @@ spec: x-kubernetes-list-map-keys: - conditionType x-kubernetes-list-type: map + taints: + description: |- + taints are the node taints that Cluster API will manage. + This list is not necessarily complete: other Kubernetes components may add or remove other taints from nodes, + e.g. the node controller might add the node.kubernetes.io/not-ready taint. + Only those taints defined in this list will be added or removed by core Cluster API controllers. + + There can be at most 64 taints. + A pod would have to tolerate all existing taints to run on the corresponding node. + + NOTE: This list is implemented as a "map" type, meaning that individual elements can be managed by different owners. + items: + description: MachineTaint defines a taint equivalent to + corev1.Taint, but additionally having a propagation field. + properties: + effect: + description: effect is the effect for the taint. Valid + values are NoSchedule, PreferNoSchedule and NoExecute. + enum: + - NoSchedule + - PreferNoSchedule + - NoExecute + type: string + key: + description: |- + key is the taint key to be applied to a node. + Must be a valid qualified name of maximum size 63 characters + with an optional subdomain prefix of maximum size 253 characters, + separated by a `/`. + maxLength: 317 + minLength: 1 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/)?([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$ + type: string + x-kubernetes-validations: + - message: key must be a valid qualified name of max + size 63 characters with an optional subdomain prefix + of max size 253 characters + rule: 'self.contains(''/'') ? ( self.split(''/'') + [0].size() <= 253 && self.split(''/'') [1].size() + <= 63 && self.split(''/'').size() == 2 ) : self.size() + <= 63' + propagation: + description: |- + propagation defines how this taint should be propagated to nodes. + Valid values are 'Always' and 'OnInitialization'. + Always: The taint will be continuously reconciled. If it is not set for a node, it will be added during reconciliation. + OnInitialization: The taint will be added during node initialization. If it gets removed from the node later on it will not get added again. + enum: + - Always + - OnInitialization + type: string + value: + description: |- + value is the taint value corresponding to the taint key. + It must be a valid label value of maximum size 63 characters. + maxLength: 63 + minLength: 1 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ + type: string + required: + - effect + - key + - propagation + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - key + - effect + x-kubernetes-list-type: map version: description: |- version defines the desired Kubernetes version. @@ -19245,7 +19654,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: machines.cluster.x-k8s.io @@ -21009,6 +21418,76 @@ spec: x-kubernetes-list-map-keys: - conditionType x-kubernetes-list-type: map + taints: + description: |- + taints are the node taints that Cluster API will manage. + This list is not necessarily complete: other Kubernetes components may add or remove other taints from nodes, + e.g. the node controller might add the node.kubernetes.io/not-ready taint. + Only those taints defined in this list will be added or removed by core Cluster API controllers. + + There can be at most 64 taints. + A pod would have to tolerate all existing taints to run on the corresponding node. + + NOTE: This list is implemented as a "map" type, meaning that individual elements can be managed by different owners. + items: + description: MachineTaint defines a taint equivalent to corev1.Taint, + but additionally having a propagation field. + properties: + effect: + description: effect is the effect for the taint. Valid values + are NoSchedule, PreferNoSchedule and NoExecute. + enum: + - NoSchedule + - PreferNoSchedule + - NoExecute + type: string + key: + description: |- + key is the taint key to be applied to a node. + Must be a valid qualified name of maximum size 63 characters + with an optional subdomain prefix of maximum size 253 characters, + separated by a `/`. + maxLength: 317 + minLength: 1 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/)?([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$ + type: string + x-kubernetes-validations: + - message: key must be a valid qualified name of max size 63 + characters with an optional subdomain prefix of max size + 253 characters + rule: 'self.contains(''/'') ? ( self.split(''/'') [0].size() + <= 253 && self.split(''/'') [1].size() <= 63 && self.split(''/'').size() + == 2 ) : self.size() <= 63' + propagation: + description: |- + propagation defines how this taint should be propagated to nodes. + Valid values are 'Always' and 'OnInitialization'. + Always: The taint will be continuously reconciled. If it is not set for a node, it will be added during reconciliation. + OnInitialization: The taint will be added during node initialization. If it gets removed from the node later on it will not get added again. + enum: + - Always + - OnInitialization + type: string + value: + description: |- + value is the taint value corresponding to the taint key. + It must be a valid label value of maximum size 63 characters. + maxLength: 63 + minLength: 1 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ + type: string + required: + - effect + - key + - propagation + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - key + - effect + x-kubernetes-list-type: map version: description: |- version defines the desired Kubernetes version. @@ -21052,7 +21531,7 @@ spec: - address - type type: object - maxItems: 32 + maxItems: 256 type: array x-kubernetes-list-type: atomic certificatesExpiryDate: @@ -21065,7 +21544,7 @@ spec: description: |- conditions represents the observations of a Machine's current state. Known condition types are Available, Ready, UpToDate, BootstrapConfigReady, InfrastructureReady, NodeReady, - NodeHealthy, Deleting, Paused. + NodeHealthy, Updating, Deleting, Paused. If a MachineHealthCheck is targeting this machine, also HealthCheckSucceeded, OwnerRemediated conditions are added. Additionally control plane Machines controlled by KubeadmControlPlane will have following additional conditions: APIServerPodHealthy, ControllerManagerPodHealthy, SchedulerPodHealthy, EtcdPodHealthy, EtcdMemberHealthy. @@ -21377,6 +21856,7 @@ spec: - Provisioning - Provisioned - Running + - Updating - Deleting - Deleted - Failed @@ -21396,7 +21876,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-system/capi-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: cluster-api name: machinesets.cluster.x-k8s.io @@ -23402,6 +23882,77 @@ spec: x-kubernetes-list-map-keys: - conditionType x-kubernetes-list-type: map + taints: + description: |- + taints are the node taints that Cluster API will manage. + This list is not necessarily complete: other Kubernetes components may add or remove other taints from nodes, + e.g. the node controller might add the node.kubernetes.io/not-ready taint. + Only those taints defined in this list will be added or removed by core Cluster API controllers. + + There can be at most 64 taints. + A pod would have to tolerate all existing taints to run on the corresponding node. + + NOTE: This list is implemented as a "map" type, meaning that individual elements can be managed by different owners. + items: + description: MachineTaint defines a taint equivalent to + corev1.Taint, but additionally having a propagation field. + properties: + effect: + description: effect is the effect for the taint. Valid + values are NoSchedule, PreferNoSchedule and NoExecute. + enum: + - NoSchedule + - PreferNoSchedule + - NoExecute + type: string + key: + description: |- + key is the taint key to be applied to a node. + Must be a valid qualified name of maximum size 63 characters + with an optional subdomain prefix of maximum size 253 characters, + separated by a `/`. + maxLength: 317 + minLength: 1 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/)?([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$ + type: string + x-kubernetes-validations: + - message: key must be a valid qualified name of max + size 63 characters with an optional subdomain prefix + of max size 253 characters + rule: 'self.contains(''/'') ? ( self.split(''/'') + [0].size() <= 253 && self.split(''/'') [1].size() + <= 63 && self.split(''/'').size() == 2 ) : self.size() + <= 63' + propagation: + description: |- + propagation defines how this taint should be propagated to nodes. + Valid values are 'Always' and 'OnInitialization'. + Always: The taint will be continuously reconciled. If it is not set for a node, it will be added during reconciliation. + OnInitialization: The taint will be added during node initialization. If it gets removed from the node later on it will not get added again. + enum: + - Always + - OnInitialization + type: string + value: + description: |- + value is the taint value corresponding to the taint key. + It must be a valid label value of maximum size 63 characters. + maxLength: 63 + minLength: 1 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ + type: string + required: + - effect + - key + - propagation + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - key + - effect + x-kubernetes-list-type: map version: description: |- version defines the desired Kubernetes version. @@ -23961,7 +24512,7 @@ spec: - --leader-elect - --diagnostics-address=${CAPI_DIAGNOSTICS_ADDRESS:=:8443} - --insecure-diagnostics=${CAPI_INSECURE_DIAGNOSTICS:=false} - - --feature-gates=MachinePool=${EXP_MACHINE_POOL:=true},ClusterResourceSet=${EXP_CLUSTER_RESOURCE_SET:=true},ClusterTopology=${CLUSTER_TOPOLOGY:=false},RuntimeSDK=${EXP_RUNTIME_SDK:=false},MachineSetPreflightChecks=${EXP_MACHINE_SET_PREFLIGHT_CHECKS:=true},MachineWaitForVolumeDetachConsiderVolumeAttachments=${EXP_MACHINE_WAITFORVOLUMEDETACH_CONSIDER_VOLUMEATTACHMENTS:=true},PriorityQueue=${EXP_PRIORITY_QUEUE:=false} + - --feature-gates=MachinePool=${EXP_MACHINE_POOL:=true},ClusterTopology=${CLUSTER_TOPOLOGY:=false},RuntimeSDK=${EXP_RUNTIME_SDK:=false},MachineSetPreflightChecks=${EXP_MACHINE_SET_PREFLIGHT_CHECKS:=true},MachineWaitForVolumeDetachConsiderVolumeAttachments=${EXP_MACHINE_WAITFORVOLUMEDETACH_CONSIDER_VOLUMEATTACHMENTS:=true},PriorityQueue=${EXP_PRIORITY_QUEUE:=false},ReconcilerRateLimiting=${EXP_RECONCILER_RATE_LIMITING:=false},InPlaceUpdates=${EXP_IN_PLACE_UPDATES:=false},MachineTaintPropagation=${EXP_MACHINE_TAINT_PROPAGATION:=false} command: - /manager env: @@ -24116,20 +24667,20 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /mutate-cluster-x-k8s-io-v1beta2-machine + path: /mutate-runtime-cluster-x-k8s-io-v1beta2-extensionconfig failurePolicy: Fail matchPolicy: Equivalent - name: default.machine.cluster.x-k8s.io + name: default.extensionconfig.runtime.addons.cluster.x-k8s.io rules: - apiGroups: - - cluster.x-k8s.io + - runtime.cluster.x-k8s.io apiVersions: - v1beta2 operations: - CREATE - UPDATE resources: - - machines + - extensionconfigs sideEffects: None - admissionReviewVersions: - v1 @@ -24138,10 +24689,10 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /mutate-cluster-x-k8s-io-v1beta2-machinedeployment + path: /mutate-cluster-x-k8s-io-v1beta2-machine failurePolicy: Fail matchPolicy: Equivalent - name: default.machinedeployment.cluster.x-k8s.io + name: default.machine.cluster.x-k8s.io rules: - apiGroups: - cluster.x-k8s.io @@ -24151,7 +24702,7 @@ webhooks: - CREATE - UPDATE resources: - - machinedeployments + - machines sideEffects: None - admissionReviewVersions: - v1 @@ -24160,10 +24711,10 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /mutate-cluster-x-k8s-io-v1beta2-machinehealthcheck + path: /mutate-cluster-x-k8s-io-v1beta2-machinedeployment failurePolicy: Fail matchPolicy: Equivalent - name: default.machinehealthcheck.cluster.x-k8s.io + name: default.machinedeployment.cluster.x-k8s.io rules: - apiGroups: - cluster.x-k8s.io @@ -24173,7 +24724,7 @@ webhooks: - CREATE - UPDATE resources: - - machinehealthchecks + - machinedeployments sideEffects: None - admissionReviewVersions: - v1 @@ -24182,10 +24733,10 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /mutate-cluster-x-k8s-io-v1beta2-machineset + path: /mutate-cluster-x-k8s-io-v1beta2-machinehealthcheck failurePolicy: Fail matchPolicy: Equivalent - name: default.machineset.cluster.x-k8s.io + name: default.machinehealthcheck.cluster.x-k8s.io rules: - apiGroups: - cluster.x-k8s.io @@ -24195,7 +24746,7 @@ webhooks: - CREATE - UPDATE resources: - - machinesets + - machinehealthchecks sideEffects: None - admissionReviewVersions: - v1 @@ -24204,20 +24755,20 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /mutate-runtime-cluster-x-k8s-io-v1beta2-extensionconfig + path: /mutate-cluster-x-k8s-io-v1beta2-machinepool failurePolicy: Fail matchPolicy: Equivalent - name: default.extensionconfig.runtime.addons.cluster.x-k8s.io + name: default.machinepool.cluster.x-k8s.io rules: - apiGroups: - - runtime.cluster.x-k8s.io + - cluster.x-k8s.io apiVersions: - v1beta2 operations: - CREATE - UPDATE resources: - - extensionconfigs + - machinepools sideEffects: None - admissionReviewVersions: - v1 @@ -24226,10 +24777,10 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /mutate-cluster-x-k8s-io-v1beta2-machinepool + path: /mutate-cluster-x-k8s-io-v1beta2-machineset failurePolicy: Fail matchPolicy: Equivalent - name: default.machinepool.cluster.x-k8s.io + name: default.machineset.cluster.x-k8s.io rules: - apiGroups: - cluster.x-k8s.io @@ -24239,7 +24790,7 @@ webhooks: - CREATE - UPDATE resources: - - machinepools + - machinesets sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 @@ -24348,20 +24899,20 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /validate-cluster-x-k8s-io-v1beta2-machine + path: /validate-runtime-cluster-x-k8s-io-v1beta2-extensionconfig failurePolicy: Fail matchPolicy: Equivalent - name: validation.machine.cluster.x-k8s.io + name: validation.extensionconfig.runtime.cluster.x-k8s.io rules: - apiGroups: - - cluster.x-k8s.io + - runtime.cluster.x-k8s.io apiVersions: - v1beta2 operations: - CREATE - UPDATE resources: - - machines + - extensionconfigs sideEffects: None - admissionReviewVersions: - v1 @@ -24370,20 +24921,21 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /validate-cluster-x-k8s-io-v1beta2-machinedeployment + path: /validate-ipam-cluster-x-k8s-io-v1beta2-ipaddress failurePolicy: Fail matchPolicy: Equivalent - name: validation.machinedeployment.cluster.x-k8s.io + name: validation.ipaddress.ipam.cluster.x-k8s.io rules: - apiGroups: - - cluster.x-k8s.io + - ipam.cluster.x-k8s.io apiVersions: - v1beta2 operations: - CREATE - UPDATE + - DELETE resources: - - machinedeployments + - ipaddresses sideEffects: None - admissionReviewVersions: - v1 @@ -24392,20 +24944,21 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /validate-cluster-x-k8s-io-v1beta2-machinedrainrule + path: /validate-ipam-cluster-x-k8s-io-v1beta2-ipaddressclaim failurePolicy: Fail matchPolicy: Equivalent - name: validation.machinedrainrule.cluster.x-k8s.io + name: validation.ipaddressclaim.ipam.cluster.x-k8s.io rules: - apiGroups: - - cluster.x-k8s.io + - ipam.cluster.x-k8s.io apiVersions: - v1beta2 operations: - CREATE - UPDATE + - DELETE resources: - - machinedrainrules + - ipaddressclaims sideEffects: None - admissionReviewVersions: - v1 @@ -24414,10 +24967,10 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /validate-cluster-x-k8s-io-v1beta2-machinehealthcheck + path: /validate-cluster-x-k8s-io-v1beta2-machine failurePolicy: Fail matchPolicy: Equivalent - name: validation.machinehealthcheck.cluster.x-k8s.io + name: validation.machine.cluster.x-k8s.io rules: - apiGroups: - cluster.x-k8s.io @@ -24427,7 +24980,7 @@ webhooks: - CREATE - UPDATE resources: - - machinehealthchecks + - machines sideEffects: None - admissionReviewVersions: - v1 @@ -24436,10 +24989,10 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /validate-cluster-x-k8s-io-v1beta2-machineset + path: /validate-cluster-x-k8s-io-v1beta2-machinedeployment failurePolicy: Fail matchPolicy: Equivalent - name: validation.machineset.cluster.x-k8s.io + name: validation.machinedeployment.cluster.x-k8s.io rules: - apiGroups: - cluster.x-k8s.io @@ -24449,7 +25002,7 @@ webhooks: - CREATE - UPDATE resources: - - machinesets + - machinedeployments sideEffects: None - admissionReviewVersions: - v1 @@ -24458,20 +25011,20 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /validate-runtime-cluster-x-k8s-io-v1beta2-extensionconfig + path: /validate-cluster-x-k8s-io-v1beta2-machinedrainrule failurePolicy: Fail matchPolicy: Equivalent - name: validation.extensionconfig.runtime.cluster.x-k8s.io + name: validation.machinedrainrule.cluster.x-k8s.io rules: - apiGroups: - - runtime.cluster.x-k8s.io + - cluster.x-k8s.io apiVersions: - v1beta2 operations: - CREATE - UPDATE resources: - - extensionconfigs + - machinedrainrules sideEffects: None - admissionReviewVersions: - v1 @@ -24480,10 +25033,10 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /validate-cluster-x-k8s-io-v1beta2-machinepool + path: /validate-cluster-x-k8s-io-v1beta2-machinehealthcheck failurePolicy: Fail matchPolicy: Equivalent - name: validation.machinepool.cluster.x-k8s.io + name: validation.machinehealthcheck.cluster.x-k8s.io rules: - apiGroups: - cluster.x-k8s.io @@ -24493,7 +25046,7 @@ webhooks: - CREATE - UPDATE resources: - - machinepools + - machinehealthchecks sideEffects: None - admissionReviewVersions: - v1 @@ -24502,21 +25055,20 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /validate-ipam-cluster-x-k8s-io-v1beta2-ipaddress + path: /validate-cluster-x-k8s-io-v1beta2-machinepool failurePolicy: Fail matchPolicy: Equivalent - name: validation.ipaddress.ipam.cluster.x-k8s.io + name: validation.machinepool.cluster.x-k8s.io rules: - apiGroups: - - ipam.cluster.x-k8s.io + - cluster.x-k8s.io apiVersions: - v1beta2 operations: - CREATE - UPDATE - - DELETE resources: - - ipaddresses + - machinepools sideEffects: None - admissionReviewVersions: - v1 @@ -24525,21 +25077,20 @@ webhooks: service: name: capi-webhook-service namespace: capi-system - path: /validate-ipam-cluster-x-k8s-io-v1beta2-ipaddressclaim + path: /validate-cluster-x-k8s-io-v1beta2-machineset failurePolicy: Fail matchPolicy: Equivalent - name: validation.ipaddressclaim.ipam.cluster.x-k8s.io + name: validation.machineset.cluster.x-k8s.io rules: - apiGroups: - - ipam.cluster.x-k8s.io + - cluster.x-k8s.io apiVersions: - v1beta2 operations: - CREATE - UPDATE - - DELETE resources: - - ipaddressclaims + - machinesets sideEffects: None --- apiVersion: v1 @@ -24555,7 +25106,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-kubeadm-bootstrap-system/capi-kubeadm-bootstrap-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: bootstrap-kubeadm cluster.x-k8s.io/v1beta1: v1beta1 @@ -25208,9 +25759,8 @@ spec: a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -25383,9 +25933,8 @@ spec: a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -26205,9 +26754,8 @@ spec: a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -26382,9 +26930,8 @@ spec: a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -26651,8 +27198,9 @@ spec: in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -26710,6 +27258,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -26865,8 +27450,9 @@ spec: in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -26924,6 +27510,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -27112,8 +27735,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -27171,6 +27795,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -27345,8 +28006,9 @@ spec: in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -27404,6 +28066,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -27872,9 +28571,8 @@ spec: a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -28273,9 +28971,8 @@ spec: a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -28772,8 +29469,9 @@ spec: in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -28831,6 +29529,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -29020,8 +29755,9 @@ spec: in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -29079,6 +29815,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -29198,6 +29971,22 @@ spec: minLength: 1 type: string type: object + encryptionAlgorithm: + description: |- + encryptionAlgorithm holds the type of asymmetric encryption algorithm used for keys and certificates. + Can be one of "RSA-2048", "RSA-3072", "RSA-4096", "ECDSA-P256" or "ECDSA-P384". + For Kubernetes 1.34 or above, "ECDSA-P384" is supported. + If not specified, Cluster API will use RSA-2048 as default. + When this field is modified every certificate generated afterward will use the new + encryptionAlgorithm. Existing CA certificates and service account keys are not rotated. + This field is only supported with Kubernetes v1.31 or above. + enum: + - ECDSA-P256 + - ECDSA-P384 + - RSA-2048 + - RSA-3072 + - RSA-4096 + type: string etcd: description: |- etcd holds configuration for etcd. @@ -29302,8 +30091,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -29361,6 +30151,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -29465,16 +30292,7 @@ spec: imageRepository: description: |- imageRepository sets the container registry to pull images from. - * If not set, the default registry of kubeadm will be used, i.e. - * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0 - * k8s.gcr.io (old registry): all older versions - Please note that when imageRepository is not set we don't allow upgrades to - versions >= v1.22.0 which use the old registry (k8s.gcr.io). Please use - a newer patch version with the new registry instead (i.e. >= v1.22.17, - >= v1.23.15, >= v1.24.9, >= v1.25.0). - * If the version is a CI build (kubernetes version starts with `ci/` or `ci-cross/`) - `gcr.io/k8s-staging-ci-images` will be used as a default for control plane components - and for kube-proxy, while `registry.k8s.io` will be used for all the other images. + If not set, the default registry of kubeadm will be used (registry.k8s.io). maxLength: 512 minLength: 1 type: string @@ -29526,8 +30344,9 @@ spec: in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -29585,6 +30404,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -30093,9 +30949,8 @@ spec: a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -30575,9 +31430,8 @@ spec: a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -31032,7 +31886,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-kubeadm-bootstrap-system/capi-kubeadm-bootstrap-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: bootstrap-kubeadm cluster.x-k8s.io/v1beta1: v1beta1 @@ -31705,9 +32559,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at + which the taint was added. format: date-time type: string value: @@ -31882,9 +32735,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at + which the taint was added. format: date-time type: string value: @@ -32658,9 +33510,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at + which the taint was added. format: date-time type: string value: @@ -32838,9 +33689,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at + which the taint was added. format: date-time type: string value: @@ -33075,8 +33925,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -33135,6 +33986,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -33295,8 +34183,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -33355,6 +34244,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -33549,8 +34475,9 @@ spec: variable present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -33610,6 +34537,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -33788,8 +34752,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -33848,6 +34813,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -34330,9 +35332,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at + which the taint was added. format: date-time type: string value: @@ -34739,9 +35740,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at + which the taint was added. format: date-time type: string value: @@ -35118,8 +36118,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -35178,6 +36179,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -35372,8 +36410,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -35432,6 +36471,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -35556,6 +36632,22 @@ spec: minLength: 1 type: string type: object + encryptionAlgorithm: + description: |- + encryptionAlgorithm holds the type of asymmetric encryption algorithm used for keys and certificates. + Can be one of "RSA-2048", "RSA-3072", "RSA-4096", "ECDSA-P256" or "ECDSA-P384". + For Kubernetes 1.34 or above, "ECDSA-P384" is supported. + If not specified, Cluster API will use RSA-2048 as default. + When this field is modified every certificate generated afterward will use the new + encryptionAlgorithm. Existing CA certificates and service account keys are not rotated. + This field is only supported with Kubernetes v1.31 or above. + enum: + - ECDSA-P256 + - ECDSA-P384 + - RSA-2048 + - RSA-3072 + - RSA-4096 + type: string etcd: description: |- etcd holds configuration for etcd. @@ -35662,8 +36754,9 @@ spec: variable present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -35723,6 +36816,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -35831,16 +36961,7 @@ spec: imageRepository: description: |- imageRepository sets the container registry to pull images from. - * If not set, the default registry of kubeadm will be used, i.e. - * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0 - * k8s.gcr.io (old registry): all older versions - Please note that when imageRepository is not set we don't allow upgrades to - versions >= v1.22.0 which use the old registry (k8s.gcr.io). Please use - a newer patch version with the new registry instead (i.e. >= v1.22.17, - >= v1.23.15, >= v1.24.9, >= v1.25.0). - * If the version is a CI build (kubernetes version starts with `ci/` or `ci-cross/`) - `gcr.io/k8s-staging-ci-images` will be used as a default for control plane components - and for kube-proxy, while `registry.k8s.io` will be used for all the other images. + If not set, the default registry of kubeadm will be used (registry.k8s.io). maxLength: 512 minLength: 1 type: string @@ -35892,8 +37013,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -35952,6 +37074,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -36472,9 +37631,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at + which the taint was added. format: date-time type: string value: @@ -36961,9 +38119,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at + which the taint was added. format: date-time type: string value: @@ -37449,7 +38606,7 @@ spec: - --leader-elect - --diagnostics-address=${CAPI_DIAGNOSTICS_ADDRESS:=:8443} - --insecure-diagnostics=${CAPI_INSECURE_DIAGNOSTICS:=false} - - --feature-gates=MachinePool=${EXP_MACHINE_POOL:=true},KubeadmBootstrapFormatIgnition=${EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION:=false},PriorityQueue=${EXP_PRIORITY_QUEUE:=false} + - --feature-gates=MachinePool=${EXP_MACHINE_POOL:=true},KubeadmBootstrapFormatIgnition=${EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION:=false},PriorityQueue=${EXP_PRIORITY_QUEUE:=false},ReconcilerRateLimiting=${EXP_RECONCILER_RATE_LIMITING:=false} - --bootstrap-token-ttl=${KUBEADM_BOOTSTRAP_TOKEN_TTL:=15m} command: - /manager @@ -37643,7 +38800,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-kubeadm-control-plane-system/capi-kubeadm-control-plane-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: control-plane-kubeadm cluster.x-k8s.io/v1beta1: v1beta1 @@ -38388,9 +39545,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -38563,9 +39719,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -39529,9 +40684,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -39707,9 +40861,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -40210,8 +41363,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -40269,6 +41423,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -40426,8 +41617,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -40485,6 +41677,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -40676,8 +41905,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -40736,6 +41966,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -40912,8 +42179,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -40971,6 +42239,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -41446,9 +42751,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -41851,9 +43155,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -42774,8 +44077,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -42833,6 +44137,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -43024,8 +44365,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -43083,6 +44425,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -43204,6 +44583,22 @@ spec: minLength: 1 type: string type: object + encryptionAlgorithm: + description: |- + encryptionAlgorithm holds the type of asymmetric encryption algorithm used for keys and certificates. + Can be one of "RSA-2048", "RSA-3072", "RSA-4096", "ECDSA-P256" or "ECDSA-P384". + For Kubernetes 1.34 or above, "ECDSA-P384" is supported. + If not specified, Cluster API will use RSA-2048 as default. + When this field is modified every certificate generated afterward will use the new + encryptionAlgorithm. Existing CA certificates and service account keys are not rotated. + This field is only supported with Kubernetes v1.31 or above. + enum: + - ECDSA-P256 + - ECDSA-P384 + - RSA-2048 + - RSA-3072 + - RSA-4096 + type: string etcd: description: |- etcd holds configuration for etcd. @@ -43309,8 +44704,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -43369,6 +44765,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -43475,16 +44908,7 @@ spec: imageRepository: description: |- imageRepository sets the container registry to pull images from. - * If not set, the default registry of kubeadm will be used, i.e. - * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0 - * k8s.gcr.io (old registry): all older versions - Please note that when imageRepository is not set we don't allow upgrades to - versions >= v1.22.0 which use the old registry (k8s.gcr.io). Please use - a newer patch version with the new registry instead (i.e. >= v1.22.17, - >= v1.23.15, >= v1.24.9, >= v1.25.0). - * If the version is a CI build (kubernetes version starts with `ci/` or `ci-cross/`) - `gcr.io/k8s-staging-ci-images` will be used as a default for control plane components - and for kube-proxy, while `registry.k8s.io` will be used for all the other images. + If not set, the default registry of kubeadm will be used (registry.k8s.io). maxLength: 512 minLength: 1 type: string @@ -43536,8 +44960,9 @@ spec: present in a Container. properties: name: - description: Name of the environment variable. Must - be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -43595,6 +45020,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -44108,9 +45570,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -44593,9 +46054,8 @@ spec: to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time at which + the taint was added. format: date-time type: string value: @@ -45166,13 +46626,7 @@ spec: type: object type: object version: - description: |- - version defines the desired Kubernetes version. - Please note that if kubeadmConfigSpec.ClusterConfiguration.imageRepository is not set - we don't allow upgrades to versions >= v1.22.0 for which kubeadm uses the old registry (k8s.gcr.io). - Please use a newer patch version with the new registry instead. The default registries of kubeadm are: - * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0 - * k8s.gcr.io (old registry): all older versions + description: version defines the desired Kubernetes version. maxLength: 256 minLength: 1 type: string @@ -45462,7 +46916,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: capi-kubeadm-control-plane-system/capi-kubeadm-control-plane-serving-cert - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 labels: cluster.x-k8s.io/provider: control-plane-kubeadm cluster.x-k8s.io/v1beta1: v1beta1 @@ -46148,9 +47602,8 @@ spec: be applied to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time + at which the taint was added. format: date-time type: string value: @@ -46328,9 +47781,8 @@ spec: be applied to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time + at which the taint was added. format: date-time type: string value: @@ -46714,8 +48166,9 @@ spec: variable present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -46775,6 +48228,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -46937,8 +48427,9 @@ spec: variable present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -46998,6 +48489,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -47194,8 +48722,9 @@ spec: variable present in a Container. properties: name: - description: Name of the environment - variable. Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -47258,6 +48787,44 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the + volume mount containing the + env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -47438,8 +49005,9 @@ spec: variable present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -47499,6 +49067,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -47986,9 +49591,8 @@ spec: be applied to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time + at which the taint was added. format: date-time type: string value: @@ -48396,9 +50000,8 @@ spec: be applied to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time + at which the taint was added. format: date-time type: string value: @@ -48960,8 +50563,9 @@ spec: variable present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -49021,6 +50625,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -49218,8 +50859,9 @@ spec: variable present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -49279,6 +50921,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -49405,6 +51084,22 @@ spec: minLength: 1 type: string type: object + encryptionAlgorithm: + description: |- + encryptionAlgorithm holds the type of asymmetric encryption algorithm used for keys and certificates. + Can be one of "RSA-2048", "RSA-3072", "RSA-4096", "ECDSA-P256" or "ECDSA-P384". + For Kubernetes 1.34 or above, "ECDSA-P384" is supported. + If not specified, Cluster API will use RSA-2048 as default. + When this field is modified every certificate generated afterward will use the new + encryptionAlgorithm. Existing CA certificates and service account keys are not rotated. + This field is only supported with Kubernetes v1.31 or above. + enum: + - ECDSA-P256 + - ECDSA-P384 + - RSA-2048 + - RSA-3072 + - RSA-4096 + type: string etcd: description: |- etcd holds configuration for etcd. @@ -49513,8 +51208,9 @@ spec: variable present in a Container. properties: name: - description: Name of the environment - variable. Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -49577,6 +51273,44 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the + volume mount containing the + env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -49687,16 +51421,7 @@ spec: imageRepository: description: |- imageRepository sets the container registry to pull images from. - * If not set, the default registry of kubeadm will be used, i.e. - * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0 - * k8s.gcr.io (old registry): all older versions - Please note that when imageRepository is not set we don't allow upgrades to - versions >= v1.22.0 which use the old registry (k8s.gcr.io). Please use - a newer patch version with the new registry instead (i.e. >= v1.22.17, - >= v1.23.15, >= v1.24.9, >= v1.25.0). - * If the version is a CI build (kubernetes version starts with `ci/` or `ci-cross/`) - `gcr.io/k8s-staging-ci-images` will be used as a default for control plane components - and for kube-proxy, while `registry.k8s.io` will be used for all the other images. + If not set, the default registry of kubeadm will be used (registry.k8s.io). maxLength: 512 minLength: 1 type: string @@ -49749,8 +51474,9 @@ spec: variable present in a Container. properties: name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -49810,6 +51536,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume + mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -50336,9 +52099,8 @@ spec: be applied to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time + at which the taint was added. format: date-time type: string value: @@ -50827,9 +52589,8 @@ spec: be applied to a node. type: string timeAdded: - description: |- - TimeAdded represents the time at which the taint was added. - It is only written for NoExecute taints. + description: TimeAdded represents the time + at which the taint was added. format: date-time type: string value: @@ -51393,12 +53154,21 @@ rules: verbs: - create - patch +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch - apiGroups: - "" resources: - secrets verbs: - create + - delete - get - list - patch @@ -51472,6 +53242,14 @@ rules: - patch - update - watch +- apiGroups: + - runtime.cluster.x-k8s.io + resources: + - extensionconfigs + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -51543,7 +53321,7 @@ spec: - --leader-elect - --diagnostics-address=${CAPI_DIAGNOSTICS_ADDRESS:=:8443} - --insecure-diagnostics=${CAPI_INSECURE_DIAGNOSTICS:=false} - - --feature-gates=MachinePool=${EXP_MACHINE_POOL:=true},ClusterTopology=${CLUSTER_TOPOLOGY:=false},KubeadmBootstrapFormatIgnition=${EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION:=false},PriorityQueue=${EXP_PRIORITY_QUEUE:=false} + - --feature-gates=MachinePool=${EXP_MACHINE_POOL:=true},ClusterTopology=${CLUSTER_TOPOLOGY:=false},KubeadmBootstrapFormatIgnition=${EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION:=false},PriorityQueue=${EXP_PRIORITY_QUEUE:=false},ReconcilerRateLimiting=${EXP_RECONCILER_RATE_LIMITING:=false},InPlaceUpdates=${EXP_IN_PLACE_UPDATES:=false} command: - /manager env: From d9fbddfbe5f66ecff0da8527422b3e1b33af9ad9 Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Mon, 1 Jun 2026 16:35:43 -0700 Subject: [PATCH 4/4] vendor: cluster-api v1.12.8 go mod vendor --- .../net/http/otelhttp/client.go | 50 - .../net/http/otelhttp/common.go | 2 +- .../net/http/otelhttp/config.go | 21 +- .../instrumentation/net/http/otelhttp/doc.go | 3 +- .../net/http/otelhttp/handler.go | 19 +- .../http/otelhttp/internal/semconv/client.go | 287 + .../net/http/otelhttp/internal/semconv/env.go | 248 - .../net/http/otelhttp/internal/semconv/gen.go | 8 +- .../otelhttp/internal/semconv/httpconv.go | 517 - .../http/otelhttp/internal/semconv/server.go | 403 + .../http/otelhttp/internal/semconv/util.go | 2 +- .../net/http/otelhttp/transport.go | 35 +- .../net/http/otelhttp/version.go | 5 +- .../go.opentelemetry.io/otel/.codespellignore | 1 + .../go.opentelemetry.io/otel/.golangci.yml | 7 + .../go.opentelemetry.io/otel/.lycheeignore | 6 +- .../go.opentelemetry.io/otel/CHANGELOG.md | 110 +- .../go.opentelemetry.io/otel/CONTRIBUTING.md | 458 +- .../vendor/go.opentelemetry.io/otel/Makefile | 3 +- .../vendor/go.opentelemetry.io/otel/README.md | 11 +- .../go.opentelemetry.io/otel/RELEASING.md | 54 +- .../go.opentelemetry.io/otel/VERSIONING.md | 2 +- .../otel/attribute/encoder.go | 2 +- .../otel/attribute/hash.go | 92 + .../otel/attribute/internal/attribute.go | 12 +- .../otel/attribute/internal/xxhash/xxhash.go | 64 + .../go.opentelemetry.io/otel/attribute/set.go | 147 +- .../otel/attribute/type_string.go | 5 +- .../otel/attribute/value.go | 3 +- .../otel/baggage/baggage.go | 16 +- .../otel/dependencies.Dockerfile | 2 +- .../otlptrace/internal/tracetransform/span.go | 16 +- .../otlp/otlptrace/otlptracegrpc/client.go | 39 +- .../otlptracegrpc/internal/counter/counter.go | 31 + .../otlptrace/otlptracegrpc/internal/gen.go | 9 + .../otlptracegrpc/internal/observ/doc.go | 6 + .../internal/observ/instrumentation.go | 342 + .../otlptracegrpc/internal/observ/target.go | 143 + .../otlptracegrpc/internal/partialsuccess.go | 11 + .../otlptracegrpc/internal/retry/retry.go | 5 + .../otlptracegrpc/internal/version.go | 8 + .../otlptracegrpc}/internal/x/README.md | 23 +- .../otlptracegrpc/internal/x/observ.go | 22 + .../otlptrace/otlptracegrpc}/internal/x/x.go | 45 +- .../otel/exporters/otlp/otlptrace/version.go | 2 +- .../otel/internal/global/instruments.go | 56 + .../otel/internal/global/meter.go | 30 +- .../vendor/go.opentelemetry.io/otel/metric.go | 2 +- .../otel/metric/asyncfloat64.go | 6 +- .../otel/metric/asyncint64.go | 6 +- .../go.opentelemetry.io/otel/metric/config.go | 38 +- .../go.opentelemetry.io/otel/metric/meter.go | 6 +- .../otel/metric/noop/noop.go | 24 + .../otel/metric/syncfloat64.go | 24 + .../otel/metric/syncint64.go | 24 + .../otel/propagation/trace_context.go | 2 +- .../otel/sdk/internal/x/features.go | 39 + .../otel/sdk/internal/x/x.go | 46 +- .../otel/sdk/resource/builtin.go | 2 +- .../otel/sdk/resource/container.go | 2 +- .../otel/sdk/resource/env.go | 2 +- .../otel/sdk/resource/host_id.go | 9 +- .../otel/sdk/resource/host_id_bsd.go | 1 - .../otel/sdk/resource/host_id_linux.go | 1 - .../otel/sdk/resource/host_id_unsupported.go | 1 - .../otel/sdk/resource/host_id_windows.go | 1 - .../otel/sdk/resource/os.go | 2 +- .../otel/sdk/resource/os_release_unix.go | 1 - .../otel/sdk/resource/os_unix.go | 1 - .../otel/sdk/resource/os_unsupported.go | 1 - .../otel/sdk/resource/process.go | 2 +- .../otel/sdk/trace/batch_span_processor.go | 107 +- .../go.opentelemetry.io/otel/sdk/trace/doc.go | 2 +- .../otel/sdk/{ => trace}/internal/env/env.go | 2 +- .../internal/observ/batch_span_processor.go | 119 + .../otel/sdk/trace/internal/observ/doc.go | 6 + .../internal/observ/simple_span_processor.go | 97 + .../otel/sdk/trace/internal/observ/tracer.go | 223 + .../otel/sdk/trace/provider.go | 48 +- .../otel/sdk/trace/sampling.go | 28 + .../otel/sdk/trace/simple_span_processor.go | 31 +- .../otel/sdk/trace/span.go | 29 +- .../otel/sdk/trace/span_limits.go | 2 +- .../otel/sdk/trace/tracer.go | 134 +- .../go.opentelemetry.io/otel/sdk/version.go | 2 +- .../otel/semconv/v1.37.0/error_type.go | 47 +- .../otel/semconv/v1.39.0/MIGRATION.md | 78 + .../otel/semconv/v1.39.0/README.md | 3 + .../otel/semconv/v1.39.0/attribute_group.go | 16239 ++++++++++++++++ .../otel/semconv/v1.39.0/doc.go | 9 + .../otel/semconv/v1.39.0/error_type.go | 56 + .../otel/semconv/v1.39.0/exception.go | 9 + .../{v1.37.0 => v1.39.0}/httpconv/metric.go | 206 +- .../{v1.37.0 => v1.39.0}/otelconv/metric.go | 364 +- .../otel/semconv/v1.39.0/schema.go | 9 + .../go.opentelemetry.io/otel/trace/auto.go | 2 +- .../go.opentelemetry.io/otel/trace/config.go | 45 +- .../go.opentelemetry.io/otel/trace/span.go | 4 + .../go.opentelemetry.io/otel/version.go | 2 +- .../go.opentelemetry.io/otel/versions.yaml | 30 +- .../vendor/go.uber.org/zap/.golangci.yml | 2 +- .../vendor/go.uber.org/zap/CHANGELOG.md | 10 + .../vendor/go.uber.org/zap/CODE_OF_CONDUCT.md | 4 +- .../vendor/go.uber.org/zap/LICENSE | 2 +- .../vendor/go.uber.org/zap/Makefile | 2 +- .../vendor/go.uber.org/zap/field.go | 10 + .../vendor/go.uber.org/zap/http_handler.go | 2 +- .../vendor/go.uber.org/zap/logger.go | 6 +- .../vendor/go.uber.org/zap/options.go | 6 +- .../vendor/go.uber.org/zap/sink.go | 2 +- .../zap/zapcore/buffered_write_syncer.go | 23 +- .../zap/zapcore/console_encoder.go | 2 +- .../vendor/go.uber.org/zap/zapcore/entry.go | 14 +- .../go.uber.org/zap/zapcore/lazy_with.go | 35 +- .../vendor/go.uber.org/zap/zapcore/level.go | 14 +- .../golang.org/x/net/http2/transport.go | 160 +- .../net/http2/writesched_priority_rfc9218.go | 15 + .../vendor/golang.org/x/net/trace/events.go | 2 +- .../vendor/golang.org/x/net/websocket/hybi.go | 1 + .../golang.org/x/sync/errgroup/errgroup.go | 4 +- .../vendor/golang.org/x/sys/unix/mkerrors.sh | 3 +- .../golang.org/x/sys/unix/zerrors_linux.go | 2 + .../x/sys/unix/zerrors_linux_386.go | 2 + .../x/sys/unix/zerrors_linux_amd64.go | 2 + .../x/sys/unix/zerrors_linux_arm.go | 2 + .../x/sys/unix/zerrors_linux_arm64.go | 2 + .../x/sys/unix/zerrors_linux_loong64.go | 2 + .../x/sys/unix/zerrors_linux_mips.go | 2 + .../x/sys/unix/zerrors_linux_mips64.go | 2 + .../x/sys/unix/zerrors_linux_mips64le.go | 2 + .../x/sys/unix/zerrors_linux_mipsle.go | 2 + .../x/sys/unix/zerrors_linux_ppc.go | 2 + .../x/sys/unix/zerrors_linux_ppc64.go | 2 + .../x/sys/unix/zerrors_linux_ppc64le.go | 2 + .../x/sys/unix/zerrors_linux_riscv64.go | 2 + .../x/sys/unix/zerrors_linux_s390x.go | 2 + .../x/sys/unix/zerrors_linux_sparc64.go | 2 + .../x/sys/unix/ztypes_netbsd_arm.go | 2 +- .../vendor/golang.org/x/term/terminal.go | 28 +- .../x/text/encoding/japanese/eucjp.go | 6 +- .../x/text/encoding/japanese/iso2022jp.go | 6 +- .../x/text/encoding/japanese/shiftjis.go | 6 +- .../x/text/encoding/korean/euckr.go | 6 +- .../x/text/encoding/simplifiedchinese/gbk.go | 20 +- .../encoding/simplifiedchinese/hzgb2312.go | 6 +- .../text/encoding/traditionalchinese/big5.go | 6 +- .../x/text/encoding/unicode/unicode.go | 6 +- .../grpc/balancer/balancer.go | 2 - .../balancer/pickfirst/internal/internal.go | 2 + .../grpc/balancer/pickfirst/pickfirst.go | 57 +- .../grpc/balancer/subconn.go | 14 - .../grpc/balancer_wrapper.go | 6 +- .../google.golang.org/grpc/clientconn.go | 164 +- .../google.golang.org/grpc/credentials/tls.go | 6 +- .../grpc/encoding/encoding.go | 4 - .../grpc/encoding/gzip/gzip.go | 12 - .../grpc/experimental/stats/metricregistry.go | 39 + .../grpc/experimental/stats/metrics.go | 76 +- .../health/grpc_health_v1/health_grpc.pb.go | 2 +- .../google.golang.org/grpc/interceptor.go | 12 +- .../balancer/gracefulswitch/gracefulswitch.go | 12 + .../grpc/internal/balancer/weight/weight.go | 66 + .../grpc/internal/envconfig/envconfig.go | 27 + .../grpc/internal/experimental.go | 7 + .../grpc/internal/idle/idle.go | 77 +- .../grpc/internal/internal.go | 29 +- .../internal/resolver/dns/dns_resolver.go | 15 +- .../internal/stats/metrics_recorder_list.go | 60 + .../grpc/internal/transport/client_stream.go | 10 +- .../grpc/internal/transport/controlbuf.go | 24 +- .../grpc/internal/transport/http2_client.go | 38 +- .../grpc/internal/transport/http2_server.go | 94 +- .../grpc/internal/transport/http_util.go | 6 - .../grpc/internal/transport/transport.go | 40 +- .../google.golang.org/grpc/mem/buffer_pool.go | 15 +- .../google.golang.org/grpc/mem/buffers.go | 65 +- .../grpc/resolver/resolver.go | 1 + .../grpc/resolver_wrapper.go | 1 + .../vendor/google.golang.org/grpc/rpc_util.go | 93 +- .../vendor/google.golang.org/grpc/server.go | 61 +- .../vendor/google.golang.org/grpc/stream.go | 87 +- .../vendor/google.golang.org/grpc/version.go | 2 +- .../protobuf/internal/encoding/tag/tag.go | 11 +- .../protobuf/internal/encoding/text/decode.go | 115 +- .../protobuf/internal/filedesc/desc.go | 1 + .../protobuf/internal/filedesc/desc_lazy.go | 22 - .../protobuf/internal/genid/descriptor_gen.go | 1 + .../protobuf/internal/impl/codec_map.go | 6 + .../protobuf/internal/impl/decode.go | 3 +- .../protobuf/internal/impl/validate.go | 26 + .../protobuf/internal/version/version.go | 2 +- .../protobuf/proto/decode.go | 10 +- .../protobuf/reflect/protodesc/desc.go | 11 +- .../protobuf/reflect/protodesc/editions.go | 4 +- .../types/descriptorpb/descriptor.pb.go | 13 +- .../types/known/timestamppb/timestamp.pb.go | 13 +- .../client-go/tools/cache/controller.go | 23 +- .../client-go/tools/cache/delta_fifo.go | 3 +- .../k8s.io/client-go/tools/cache/reflector.go | 8 +- .../reflector_data_consistency_detector.go | 4 +- .../client-go/tools/cache/shared_informer.go | 11 +- .../client-go/tools/cache/the_real_fifo.go | 3 +- .../data_consistency_detector.go | 23 +- cluster-api/cluster-api/vendor/modules.txt | 104 +- .../sigs.k8s.io/cluster-api/.dockerignore | 2 + .../vendor/sigs.k8s.io/cluster-api/.gitignore | 4 +- .../sigs.k8s.io/cluster-api/.golangci-kal.yml | 45 +- .../sigs.k8s.io/cluster-api/.golangci.yml | 39 +- .../sigs.k8s.io/cluster-api/.govuln_exclude | 2 + .../sigs.k8s.io/cluster-api/.trivyignore | 8 + .../sigs.k8s.io/cluster-api/CONTRIBUTING.md | 12 +- .../vendor/sigs.k8s.io/cluster-api/Makefile | 129 +- .../sigs.k8s.io/cluster-api/OWNERS_ALIASES | 24 +- .../vendor/sigs.k8s.io/cluster-api/Tiltfile | 25 +- .../kubeadm/v1beta1/.import-restrictions | 5 + .../kubeadm/v1beta1/condition_consts.go | 63 + .../bootstrap/kubeadm/v1beta1/conversion.go | 998 + .../bootstrap/kubeadm/v1beta1}/doc.go | 9 +- .../kubeadm/v1beta1/groupversion_info.go | 45 + .../kubeadm/v1beta1/kubeadm_types.go | 893 + .../kubeadm/v1beta1/kubeadmconfig_types.go | 850 + .../v1beta1/kubeadmconfigtemplate_types.go | 77 + .../v1beta1/v1beta2_condition_consts.go | 63 + .../v1beta1/zz_generated.conversion.go | 1814 ++ .../kubeadm/v1beta1/zz_generated.deepcopy.go | 1353 ++ .../kubeadm/v1beta2/kubeadm_types.go | 43 +- .../kubeadm/v1beta1/.import-restrictions | 5 + .../kubeadm/v1beta1/condition_consts.go | 153 + .../kubeadm/v1beta1/conversion.go | 595 + .../controlplane/kubeadm/v1beta1}/doc.go | 11 +- .../kubeadm/v1beta1/groupversion_info.go | 45 + .../v1beta1/kubeadm_control_plane_types.go | 497 + .../kubeadmcontrolplanetemplate_types.go | 153 + .../v1beta1/v1beta2_condition_consts.go | 362 + .../v1beta1/zz_generated.conversion.go | 589 + .../kubeadm/v1beta1/zz_generated.deepcopy.go | 533 + .../v1beta2/kubeadm_control_plane_types.go | 9 - .../api/core/v1beta1/cluster_types.go | 4 +- .../api/core/v1beta1/conversion.go | 66 +- .../api/core/v1beta1/machine_types.go | 8 +- .../core/v1beta1/zz_generated.conversion.go | 3 + .../api/core/v1beta1/zz_generated.openapi.go | 1 - .../api/core/v1beta2/cluster_types.go | 39 + .../api/core/v1beta2/clusterclass_types.go | 54 + .../api/core/v1beta2/common_types.go | 64 +- .../api/core/v1beta2/machine_phase_types.go | 4 + .../api/core/v1beta2/machine_types.go | 69 +- .../core/v1beta2/machinehealthcheck_types.go | 38 +- .../api/core/v1beta2/machineset_types.go | 23 + .../core/v1beta2/v1beta1_condition_consts.go | 25 + .../api/core/v1beta2/zz_generated.deepcopy.go | 112 + .../api/core/v1beta2/zz_generated.openapi.go | 286 +- .../runtime/hooks/v1alpha1/common_types.go | 12 + .../hooks/v1alpha1/inplaceupdate_types.go | 254 + .../hooks/v1alpha1/lifecyclehooks_types.go | 204 +- .../hooks/v1alpha1/topologymutation_types.go | 12 - .../hooks/v1alpha1/upgrade_plan_types.go | 129 + .../hooks/v1alpha1/zz_generated.deepcopy.go | 531 +- .../hooks/v1alpha1/zz_generated.openapi.go | 1512 +- .../cluster-api/cloudbuild-nightly.yaml | 2 +- .../sigs.k8s.io/cluster-api/cloudbuild.yaml | 2 +- .../cluster-api/controllers/alias.go | 58 +- .../clustercache/cluster_accessor.go | 33 +- .../clustercache/cluster_accessor_client.go | 26 +- .../controllers/clustercache/cluster_cache.go | 27 +- .../clustercache/cluster_cache_fake.go | 10 +- .../controllers/crdmigrator/crd_migrator.go | 5 +- .../cluster-api/exp/controllers/alias.go | 47 - .../exp/ipam/internal/webhooks/doc.go | 18 - .../cluster-api/exp/ipam/webhooks/alias.go | 45 - .../cluster-api/exp/ipam/webhooks/doc.go | 18 - .../cluster-api/exp/runtime/client/client.go | 9 +- .../exp/runtime/controllers/alias.go | 48 - .../exp/runtime/internal/controllers/doc.go | 18 - .../topology/desiredstate/desired_state.go | 448 +- .../topology/desiredstate/lifecycle_hooks.go | 420 + .../exp/topology/desiredstate/upgrade_plan.go | 490 + .../exp/topology/scope/blueprint.go | 20 +- .../exp/topology/scope/hookresponsetracker.go | 13 +- .../exp/topology/scope/upgradetracker.go | 69 +- .../sigs.k8s.io/cluster-api/exp/util/util.go | 120 - .../cluster-api/exp/webhooks/alias.go | 31 - .../cluster-api/exp/webhooks/doc.go | 18 - .../cluster-api/feature/feature.go | 29 +- .../internal/api/core/v1alpha3/conversion.go | 6 + .../core/v1alpha3/zz_generated.conversion.go | 1 + .../internal/api/core/v1alpha4/conversion.go | 8 + .../core/v1alpha4/zz_generated.conversion.go | 3 + .../internal/contract/controlplane.go | 31 +- .../controllers/cluster/cluster_controller.go | 14 +- .../cluster/cluster_controller_phases.go | 2 +- .../cluster/cluster_controller_status.go | 8 +- .../clusterclass/clusterclass_controller.go | 9 +- .../clusterresourceset_controller.go | 14 +- .../clusterresourcesetbinding_controller.go | 5 +- .../controllers/extensionconfig}/doc.go | 4 +- .../extensionconfig_controller.go | 166 +- .../controllers/extensionconfig}/index.go | 2 +- .../controllers/extensionconfig}/warmup.go | 57 +- .../controllers/machine/drain/drain.go | 2 +- .../controllers/machine/machine_controller.go | 148 +- .../machine_controller_inplace_update.go | 284 + .../machine/machine_controller_noderef.go | 198 +- .../machine/machine_controller_phases.go | 36 +- .../machine/machine_controller_status.go | 141 +- .../machinedeployment_canupdatemachineset.go | 383 + .../machinedeployment_controller.go | 232 +- .../machinedeployment_rolling.go | 326 - .../machinedeployment_rollout_ondelete.go | 190 +- .../machinedeployment_rollout_planner.go | 449 + ...machinedeployment_rollout_rollingupdate.go | 630 + .../machinedeployment_status.go | 95 +- .../machinedeployment_sync.go | 336 +- .../machinedeployment/mdutil/util.go | 277 +- .../machinehealthcheck_controller.go | 22 +- .../machinehealthcheck_targets.go | 221 +- .../internal/controllers/machinepool/OWNERS | 8 + .../controllers/machinepool}/doc.go | 4 +- .../machinepool}/machinepool_controller.go | 41 +- .../machinepool_controller_noderef.go | 44 +- .../machinepool_controller_phases.go | 27 +- .../machinepool_controller_scope.go | 2 +- .../machinepool_controller_status.go | 4 +- .../machineset/machineset_controller.go | 1106 +- .../machineset_controller_status.go | 16 +- .../machineset/machineset_deletion_order.go | 53 +- .../machineset/machineset_preflight.go | 15 +- .../topology/cluster/cluster_controller.go | 122 +- .../topology/cluster/conditions.go | 298 +- .../cluster/managedfieldsmitigation.go | 110 + .../topology/cluster/patches/engine.go | 3 +- .../topology/cluster/patches/patch.go | 94 +- .../topology/cluster/reconcile_state.go | 207 +- .../cluster/structuredmerge/dryrun.go | 68 +- .../cluster/structuredmerge/interfaces.go | 9 +- .../cluster/structuredmerge/options.go | 30 +- .../structuredmerge/serversidepathhelper.go | 32 +- .../machinedeployment_controller.go | 17 +- .../machineset/machineset_controller.go | 56 +- .../cluster-api/internal/hooks/tracking.go | 83 +- .../internal/runtime/client/client.go | 251 +- .../internal/util/client/client.go | 295 + .../internal/util/client/metrics.go | 35 + .../internal/util/controller/builder.go | 322 + .../internal/util/controller/controller.go | 149 + .../util/controller/controller_fake.go | 47 + .../internal/util/controller/metrics.go | 67 + .../internal/util/inplace/inplace.go | 69 + .../cluster-api/internal/util/patch/patch.go | 249 + .../cluster-api/internal/util/ssa/cache.go | 16 +- .../internal/util/ssa/filterintent.go | 120 +- .../internal/util/ssa/managedfields.go | 282 +- .../util/ssa/managedfieldsmitigation.go | 245 + .../cluster-api/internal/util/ssa/matchers.go | 113 - .../cluster-api/internal/util/ssa/patch.go | 44 +- .../cluster-api/internal/webhooks/cluster.go | 241 +- .../internal/webhooks/clusterclass.go | 72 + ...ceset_webhook.go => clusterresourceset.go} | 10 - ...ebhook.go => clusterresourcesetbinding.go} | 10 - ...onconfig_webhook.go => extensionconfig.go} | 2 +- .../ipam => }/internal/webhooks/ipaddress.go | 0 .../internal/webhooks/ipaddressclaim.go | 0 .../cluster-api/internal/webhooks/machine.go | 65 + .../internal/webhooks/machinedeployment.go | 3 + .../internal/webhooks/machinepool.go | 9 +- .../internal/webhooks/machineset.go | 3 + .../internal/webhooks/patch_validation.go | 4 +- .../internal/webhooks/runtime/doc.go | 18 - .../vendor/sigs.k8s.io/cluster-api/main.go | 72 +- .../sigs.k8s.io/cluster-api/metadata.yaml | 3 + .../sigs.k8s.io/cluster-api/netlify.toml | 2 +- .../cluster-api/util/cache/cache.go | 91 +- .../cluster-api/util/certs/certs.go | 74 + .../cluster-api/util/certs/types.go | 3 +- .../util/collections/machine_filters.go | 9 +- .../cluster-api/util/conditions/setter.go | 4 +- .../cluster-api/util/conditions/sort.go | 2 + .../cluster-api/util/kubeconfig/kubeconfig.go | 30 +- .../cluster-api/util/kubeconfig/options.go | 46 + .../cluster-api/util/patch/patch.go | 26 +- .../cluster-api/util/patch/utils.go | 7 +- .../cluster-api/util/resource/resource.go | 2 +- .../cluster-api/util/secret/certificates.go | 92 +- .../sigs.k8s.io/cluster-api/util/util.go | 104 +- .../cluster-api/util/version/version.go | 2 +- .../sigs.k8s.io/cluster-api/webhooks/alias.go | 32 +- .../v6/schema/elements.go | 47 +- .../structured-merge-diff/v6/typed/remove.go | 65 +- .../v6/value/reflectcache.go | 4 + vendor/go.uber.org/zap/.golangci.yml | 2 +- vendor/go.uber.org/zap/CHANGELOG.md | 10 + vendor/go.uber.org/zap/CODE_OF_CONDUCT.md | 4 +- vendor/go.uber.org/zap/LICENSE | 2 +- vendor/go.uber.org/zap/Makefile | 2 +- vendor/go.uber.org/zap/field.go | 10 + vendor/go.uber.org/zap/http_handler.go | 2 +- vendor/go.uber.org/zap/logger.go | 6 +- vendor/go.uber.org/zap/options.go | 6 +- vendor/go.uber.org/zap/sink.go | 2 +- .../zap/zapcore/buffered_write_syncer.go | 23 +- .../zap/zapcore/console_encoder.go | 2 +- vendor/go.uber.org/zap/zapcore/entry.go | 14 +- vendor/go.uber.org/zap/zapcore/lazy_with.go | 35 +- vendor/go.uber.org/zap/zapcore/level.go | 14 +- vendor/modules.txt | 8 +- .../kubeadm/v1beta2/kubeadm_types.go | 43 +- .../api/core/v1beta1/cluster_types.go | 4 +- .../api/core/v1beta1/conversion.go | 66 +- .../api/core/v1beta1/machine_types.go | 8 +- .../core/v1beta1/zz_generated.conversion.go | 3 + .../api/core/v1beta1/zz_generated.openapi.go | 1 - .../api/core/v1beta2/cluster_types.go | 39 + .../api/core/v1beta2/clusterclass_types.go | 54 + .../api/core/v1beta2/common_types.go | 62 + .../api/core/v1beta2/machine_phase_types.go | 4 + .../api/core/v1beta2/machine_types.go | 69 +- .../core/v1beta2/machinehealthcheck_types.go | 38 +- .../api/core/v1beta2/machineset_types.go | 23 + .../core/v1beta2/v1beta1_condition_consts.go | 25 + .../api/core/v1beta2/zz_generated.deepcopy.go | 112 + .../api/core/v1beta2/zz_generated.openapi.go | 286 +- .../cluster-api/feature/feature.go | 29 +- .../cluster-api/util/certs/certs.go | 74 + .../cluster-api/util/certs/types.go | 3 +- .../cluster-api/util/conditions/sort.go | 2 + .../cluster-api/util/kubeconfig/kubeconfig.go | 30 +- .../cluster-api/util/kubeconfig/options.go | 46 + .../cluster-api/util/patch/patch.go | 26 +- .../cluster-api/util/patch/utils.go | 5 - .../cluster-api/util/secret/certificates.go | 92 +- vendor/sigs.k8s.io/cluster-api/util/util.go | 104 +- 431 files changed, 44012 insertions(+), 6159 deletions(-) delete mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/client.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/client.go delete mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/env.go delete mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/httpconv.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/server.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/hash.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/internal/xxhash/xxhash.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter/counter.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/doc.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/instrumentation.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/target.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/version.go rename cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/{sdk/trace => exporters/otlp/otlptrace/otlptracegrpc}/internal/x/README.md (54%) create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/observ.go rename cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/{sdk/trace => exporters/otlp/otlptrace/otlptracegrpc}/internal/x/x.go (55%) create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go rename cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/{ => trace}/internal/env/env.go (98%) create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/doc.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/MIGRATION.md create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/README.md create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/attribute_group.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/doc.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/error_type.go create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/exception.go rename cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/{v1.37.0 => v1.39.0}/httpconv/metric.go (92%) rename cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/{v1.37.0 => v1.39.0}/otelconv/metric.go (89%) create mode 100644 cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/schema.go create mode 100644 cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/balancer/weight/weight.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.govuln_exclude create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.trivyignore create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/.import-restrictions create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/condition_consts.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/conversion.go rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/internal/controllers => api/bootstrap/kubeadm/v1beta1}/doc.go (60%) create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/groupversion_info.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/kubeadm_types.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/kubeadmconfig_types.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/kubeadmconfigtemplate_types.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/v1beta2_condition_consts.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/zz_generated.conversion.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/zz_generated.deepcopy.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/.import-restrictions create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/condition_consts.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/conversion.go rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/internal/webhooks => api/controlplane/kubeadm/v1beta1}/doc.go (56%) create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/groupversion_info.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/kubeadm_control_plane_types.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/kubeadmcontrolplanetemplate_types.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/v1beta2_condition_consts.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/zz_generated.conversion.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/zz_generated.deepcopy.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/inplaceupdate_types.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/upgrade_plan_types.go delete mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/controllers/alias.go delete mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/internal/webhooks/doc.go delete mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/webhooks/alias.go delete mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/webhooks/doc.go delete mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/controllers/alias.go delete mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/doc.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/desiredstate/lifecycle_hooks.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/desiredstate/upgrade_plan.go delete mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/util/util.go delete mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/webhooks/alias.go delete mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/webhooks/doc.go rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/runtime/controllers => internal/controllers/extensionconfig}/doc.go (85%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/runtime/internal/controllers => internal/controllers/extensionconfig}/extensionconfig_controller.go (63%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/runtime/internal/controllers => internal/controllers/extensionconfig}/index.go (98%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/runtime/internal/controllers => internal/controllers/extensionconfig}/warmup.go (71%) create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_inplace_update.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_canupdatemachineset.go delete mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rolling.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rollout_planner.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rollout_rollingupdate.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/OWNERS rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/controllers => internal/controllers/machinepool}/doc.go (87%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/internal/controllers => internal/controllers/machinepool}/machinepool_controller.go (90%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/internal/controllers => internal/controllers/machinepool}/machinepool_controller_noderef.go (83%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/internal/controllers => internal/controllers/machinepool}/machinepool_controller_phases.go (94%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/internal/controllers => internal/controllers/machinepool}/machinepool_controller_scope.go (99%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/internal/controllers => internal/controllers/machinepool}/machinepool_controller_status.go (95%) create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/managedfieldsmitigation.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/client/client.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/client/metrics.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/builder.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/controller.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/controller_fake.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/metrics.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/inplace/inplace.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/patch/patch.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/managedfieldsmitigation.go delete mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/matchers.go rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/{clusterresourceset_webhook.go => clusterresourceset.go} (93%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/{clusterresourcesetbinding_webhook.go => clusterresourcesetbinding.go} (90%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/{runtime/extensionconfig_webhook.go => extensionconfig.go} (99%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/ipam => }/internal/webhooks/ipaddress.go (100%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp/ipam => }/internal/webhooks/ipaddressclaim.go (100%) rename cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/{exp => }/internal/webhooks/machinepool.go (96%) delete mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/runtime/doc.go create mode 100644 cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/options.go create mode 100644 vendor/sigs.k8s.io/cluster-api/util/kubeconfig/options.go diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/client.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/client.go deleted file mode 100644 index 521daa25db..0000000000 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/client.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" - -import ( - "context" - "io" - "net/http" - "net/url" - "strings" -) - -// DefaultClient is the default Client and is used by Get, Head, Post and PostForm. -// Please be careful of initialization order - for example, if you change -// the global propagator, the DefaultClient might still be using the old one. -var DefaultClient = &http.Client{Transport: NewTransport(http.DefaultTransport)} - -// Get is a convenient replacement for http.Get that adds a span around the request. -func Get(ctx context.Context, targetURL string) (resp *http.Response, err error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, targetURL, http.NoBody) - if err != nil { - return nil, err - } - return DefaultClient.Do(req) -} - -// Head is a convenient replacement for http.Head that adds a span around the request. -func Head(ctx context.Context, targetURL string) (resp *http.Response, err error) { - req, err := http.NewRequestWithContext(ctx, http.MethodHead, targetURL, http.NoBody) - if err != nil { - return nil, err - } - return DefaultClient.Do(req) -} - -// Post is a convenient replacement for http.Post that adds a span around the request. -func Post(ctx context.Context, targetURL, contentType string, body io.Reader) (resp *http.Response, err error) { - req, err := http.NewRequestWithContext(ctx, http.MethodPost, targetURL, body) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", contentType) - return DefaultClient.Do(req) -} - -// PostForm is a convenient replacement for http.PostForm that adds a span around the request. -func PostForm(ctx context.Context, targetURL string, data url.Values) (resp *http.Response, err error) { - return Post(ctx, targetURL, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) -} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go index a83a026274..3ae0824344 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go @@ -23,5 +23,5 @@ const ( type Filter func(*http.Request) bool func newTracer(tp trace.TracerProvider) trace.Tracer { - return tp.Tracer(ScopeName, trace.WithInstrumentationVersion(Version())) + return tp.Tracer(ScopeName, trace.WithInstrumentationVersion(Version)) } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go index 38fb79c032..f7338b7e52 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go @@ -26,7 +26,6 @@ type config struct { Meter metric.Meter Propagators propagation.TextMapPropagator SpanStartOptions []trace.SpanStartOption - PublicEndpoint bool PublicEndpointFn func(*http.Request) bool ReadEvent bool WriteEvent bool @@ -67,7 +66,7 @@ func newConfig(opts ...Option) *config { c.Meter = c.MeterProvider.Meter( ScopeName, - metric.WithInstrumentationVersion(Version()), + metric.WithInstrumentationVersion(Version), ) return c @@ -93,20 +92,10 @@ func WithMeterProvider(provider metric.MeterProvider) Option { }) } -// WithPublicEndpoint configures the Handler to link the span with an incoming -// span context. If this option is not provided, then the association is a child -// association instead of a link. -func WithPublicEndpoint() Option { - return optionFunc(func(c *config) { - c.PublicEndpoint = true - }) -} - // WithPublicEndpointFn runs with every request, and allows conditionally // configuring the Handler to link the span with an incoming span context. If // this option is not provided or returns false, then the association is a // child association instead of a link. -// Note: WithPublicEndpoint takes precedence over WithPublicEndpointFn. func WithPublicEndpointFn(fn func(*http.Request) bool) Option { return optionFunc(func(c *config) { c.PublicEndpointFn = fn @@ -143,11 +132,13 @@ func WithFilter(f Filter) Option { }) } -type event int +// Event represents message event types for [WithMessageEvents]. +type Event int // Different types of events that can be recorded, see WithMessageEvents. const ( - ReadEvents event = iota + unspecifiedEvents Event = iota + ReadEvents WriteEvents ) @@ -160,7 +151,7 @@ const ( // using the ReadBytesKey // - WriteEvents: Record the number of bytes written after every http.ResponeWriter.Write // using the WriteBytesKey -func WithMessageEvents(events ...event) Option { +func WithMessageEvents(events ...Event) Option { return optionFunc(func(c *config) { for _, e := range events { switch e { diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/doc.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/doc.go index 56b24b982a..1c9aa3ff42 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/doc.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/doc.go @@ -2,6 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 // Package otelhttp provides an http.Handler and functions that are intended -// to be used to add tracing by wrapping existing handlers (with Handler) and -// routes WithRouteTag. +// to be used to add tracing by wrapping existing handlers. package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go index fef83b42fe..1ecd4be2dd 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go @@ -29,7 +29,6 @@ type middleware struct { writeEvent bool filters []Filter spanNameFormatter func(string, *http.Request) string - publicEndpoint bool publicEndpointFn func(*http.Request) bool metricAttributesFn func(*http.Request) []attribute.KeyValue @@ -77,7 +76,6 @@ func (h *middleware) configure(c *config) { h.writeEvent = c.WriteEvent h.filters = c.Filters h.spanNameFormatter = c.SpanNameFormatter - h.publicEndpoint = c.PublicEndpoint h.publicEndpointFn = c.PublicEndpointFn h.server = c.ServerName h.semconv = semconv.NewHTTPServer(c.Meter) @@ -102,7 +100,7 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http } opts = append(opts, h.spanStartOptions...) - if h.publicEndpoint || (h.publicEndpointFn != nil && h.publicEndpointFn(r.WithContext(ctx))) { + if h.publicEndpointFn != nil && h.publicEndpointFn(r.WithContext(ctx)) { opts = append(opts, trace.WithNewRoot()) // Linking incoming span context if any for public endpoint. if s := trace.SpanContextFromContext(ctx); s.IsValid() && s.IsRemote() { @@ -221,18 +219,3 @@ func (h *middleware) metricAttributesFromRequest(r *http.Request) []attribute.Ke } return attributeForRequest } - -// WithRouteTag annotates spans and metrics with the provided route name -// with HTTP route attribute. -func WithRouteTag(route string, h http.Handler) http.Handler { - attr := semconv.NewHTTPServer(nil).Route(route) - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - span := trace.SpanFromContext(r.Context()) - span.SetAttributes(attr) - - labeler, _ := LabelerFromContext(r.Context()) - labeler.Add(attr) - - h.ServeHTTP(w, r) - }) -} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/client.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/client.go new file mode 100644 index 0000000000..29d6f508c1 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/client.go @@ -0,0 +1,287 @@ +// Code generated by gotmpl. DO NOT MODIFY. +// source: internal/shared/semconv/client.go.tmpl + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package semconv provides OpenTelemetry semantic convention types and +// functionality. +package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv" + +import ( + "context" + "fmt" + "net/http" + "slices" + "strconv" + "strings" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/semconv/v1.39.0" + "go.opentelemetry.io/otel/semconv/v1.39.0/httpconv" +) + +type HTTPClient struct{ + requestBodySize httpconv.ClientRequestBodySize + requestDuration httpconv.ClientRequestDuration +} + +func NewHTTPClient(meter metric.Meter) HTTPClient { + client := HTTPClient{} + + var err error + client.requestBodySize, err = httpconv.NewClientRequestBodySize(meter) + handleErr(err) + + client.requestDuration, err = httpconv.NewClientRequestDuration( + meter, + metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10), + ) + handleErr(err) + + return client +} + +func (n HTTPClient) Status(code int) (codes.Code, string) { + if code < 100 || code >= 600 { + return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) + } + if code >= 400 { + return codes.Error, "" + } + return codes.Unset, "" +} + +// RequestTraceAttrs returns trace attributes for an HTTP request made by a client. +func (n HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue { + /* + below attributes are returned: + - http.request.method + - http.request.method.original + - url.full + - server.address + - server.port + - network.protocol.name + - network.protocol.version + */ + numOfAttributes := 3 // URL, server address, proto, and method. + + var urlHost string + if req.URL != nil { + urlHost = req.URL.Host + } + var requestHost string + var requestPort int + for _, hostport := range []string{urlHost, req.Header.Get("Host")} { + requestHost, requestPort = SplitHostPort(hostport) + if requestHost != "" || requestPort > 0 { + break + } + } + + eligiblePort := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort) + if eligiblePort > 0 { + numOfAttributes++ + } + useragent := req.UserAgent() + if useragent != "" { + numOfAttributes++ + } + + protoName, protoVersion := netProtocol(req.Proto) + if protoName != "" && protoName != "http" { + numOfAttributes++ + } + if protoVersion != "" { + numOfAttributes++ + } + + method, originalMethod := n.method(req.Method) + if originalMethod != (attribute.KeyValue{}) { + numOfAttributes++ + } + + attrs := make([]attribute.KeyValue, 0, numOfAttributes) + + attrs = append(attrs, method) + if originalMethod != (attribute.KeyValue{}) { + attrs = append(attrs, originalMethod) + } + + var u string + if req.URL != nil { + // Remove any username/password info that may be in the URL. + userinfo := req.URL.User + req.URL.User = nil + u = req.URL.String() + // Restore any username/password info that was removed. + req.URL.User = userinfo + } + attrs = append(attrs, semconv.URLFull(u)) + + attrs = append(attrs, semconv.ServerAddress(requestHost)) + if eligiblePort > 0 { + attrs = append(attrs, semconv.ServerPort(eligiblePort)) + } + + if protoName != "" && protoName != "http" { + attrs = append(attrs, semconv.NetworkProtocolName(protoName)) + } + if protoVersion != "" { + attrs = append(attrs, semconv.NetworkProtocolVersion(protoVersion)) + } + + return attrs +} + +// ResponseTraceAttrs returns trace attributes for an HTTP response made by a client. +func (n HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue { + /* + below attributes are returned: + - http.response.status_code + - error.type + */ + var count int + if resp.StatusCode > 0 { + count++ + } + + if isErrorStatusCode(resp.StatusCode) { + count++ + } + + attrs := make([]attribute.KeyValue, 0, count) + if resp.StatusCode > 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(resp.StatusCode)) + } + + if isErrorStatusCode(resp.StatusCode) { + errorType := strconv.Itoa(resp.StatusCode) + attrs = append(attrs, semconv.ErrorTypeKey.String(errorType)) + } + return attrs +} + +func (n HTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) { + if method == "" { + return semconv.HTTPRequestMethodGet, attribute.KeyValue{} + } + if attr, ok := methodLookup[method]; ok { + return attr, attribute.KeyValue{} + } + + orig := semconv.HTTPRequestMethodOriginal(method) + if attr, ok := methodLookup[strings.ToUpper(method)]; ok { + return attr, orig + } + return semconv.HTTPRequestMethodGet, orig +} + +func (n HTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue { + num := len(additionalAttributes) + 2 + var h string + if req.URL != nil { + h = req.URL.Host + } + var requestHost string + var requestPort int + for _, hostport := range []string{h, req.Header.Get("Host")} { + requestHost, requestPort = SplitHostPort(hostport) + if requestHost != "" || requestPort > 0 { + break + } + } + + port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort) + if port > 0 { + num++ + } + + protoName, protoVersion := netProtocol(req.Proto) + if protoName != "" { + num++ + } + if protoVersion != "" { + num++ + } + + if statusCode > 0 { + num++ + } + + attributes := slices.Grow(additionalAttributes, num) + attributes = append(attributes, + semconv.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)), + semconv.ServerAddress(requestHost), + n.scheme(req), + ) + + if port > 0 { + attributes = append(attributes, semconv.ServerPort(port)) + } + if protoName != "" { + attributes = append(attributes, semconv.NetworkProtocolName(protoName)) + } + if protoVersion != "" { + attributes = append(attributes, semconv.NetworkProtocolVersion(protoVersion)) + } + + if statusCode > 0 { + attributes = append(attributes, semconv.HTTPResponseStatusCode(statusCode)) + } + return attributes +} + +type MetricOpts struct { + measurement metric.MeasurementOption + addOptions metric.AddOption +} + +func (o MetricOpts) MeasurementOption() metric.MeasurementOption { + return o.measurement +} + +func (o MetricOpts) AddOptions() metric.AddOption { + return o.addOptions +} + +func (n HTTPClient) MetricOptions(ma MetricAttributes) map[string]MetricOpts { + opts := map[string]MetricOpts{} + + attributes := n.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes) + set := metric.WithAttributeSet(attribute.NewSet(attributes...)) + opts["new"] = MetricOpts{ + measurement: set, + addOptions: set, + } + + return opts +} + +func (n HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts map[string]MetricOpts) { + n.requestBodySize.Inst().Record(ctx, md.RequestSize, opts["new"].MeasurementOption()) + n.requestDuration.Inst().Record(ctx, md.ElapsedTime/1000, opts["new"].MeasurementOption()) +} + +// TraceAttributes returns attributes for httptrace. +func (n HTTPClient) TraceAttributes(host string) []attribute.KeyValue { + return []attribute.KeyValue{ + semconv.ServerAddress(host), + } +} + +func (n HTTPClient) scheme(req *http.Request) attribute.KeyValue { + if req.URL != nil && req.URL.Scheme != "" { + return semconv.URLScheme(req.URL.Scheme) + } + if req.TLS != nil { + return semconv.URLScheme("https") + } + return semconv.URLScheme("http") +} + +func isErrorStatusCode(code int) bool { + return code >= 400 || code < 100 +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/env.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/env.go deleted file mode 100644 index 821b80ec41..0000000000 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/env.go +++ /dev/null @@ -1,248 +0,0 @@ -// Code generated by gotmpl. DO NOT MODIFY. -// source: internal/shared/semconv/env.go.tmpl - -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv" - -import ( - "context" - "fmt" - "net/http" - "strings" - "sync" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/semconv/v1.37.0/httpconv" -) - -// OTelSemConvStabilityOptIn is an environment variable. -// That can be set to "http/dup" to keep getting the old HTTP semantic conventions. -const OTelSemConvStabilityOptIn = "OTEL_SEMCONV_STABILITY_OPT_IN" - -type ResponseTelemetry struct { - StatusCode int - ReadBytes int64 - ReadError error - WriteBytes int64 - WriteError error -} - -type HTTPServer struct { - requestBodySizeHistogram httpconv.ServerRequestBodySize - responseBodySizeHistogram httpconv.ServerResponseBodySize - requestDurationHistogram httpconv.ServerRequestDuration -} - -// RequestTraceAttrs returns trace attributes for an HTTP request received by a -// server. -// -// The server must be the primary server name if it is known. For example this -// would be the ServerName directive -// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache -// server, and the server_name directive -// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an -// nginx server. More generically, the primary server name would be the host -// header value that matches the default virtual host of an HTTP server. It -// should include the host identifier and if a port is used to route to the -// server that port identifier should be included as an appropriate port -// suffix. -// -// If the primary server name is not known, server should be an empty string. -// The req Host will be used to determine the server instead. -func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue { - return CurrentHTTPServer{}.RequestTraceAttrs(server, req, opts) -} - -func (s HTTPServer) NetworkTransportAttr(network string) []attribute.KeyValue { - return []attribute.KeyValue{ - CurrentHTTPServer{}.NetworkTransportAttr(network), - } -} - -// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP response. -// -// If any of the fields in the ResponseTelemetry are not set the attribute will be omitted. -func (s HTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue { - return CurrentHTTPServer{}.ResponseTraceAttrs(resp) -} - -// Route returns the attribute for the route. -func (s HTTPServer) Route(route string) attribute.KeyValue { - return CurrentHTTPServer{}.Route(route) -} - -// Status returns a span status code and message for an HTTP status code -// value returned by a server. Status codes in the 400-499 range are not -// returned as errors. -func (s HTTPServer) Status(code int) (codes.Code, string) { - if code < 100 || code >= 600 { - return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) - } - if code >= 500 { - return codes.Error, "" - } - return codes.Unset, "" -} - -type ServerMetricData struct { - ServerName string - ResponseSize int64 - - MetricData - MetricAttributes -} - -type MetricAttributes struct { - Req *http.Request - StatusCode int - AdditionalAttributes []attribute.KeyValue -} - -type MetricData struct { - RequestSize int64 - - // The request duration, in milliseconds - ElapsedTime float64 -} - -var ( - metricAddOptionPool = &sync.Pool{ - New: func() any { - return &[]metric.AddOption{} - }, - } - - metricRecordOptionPool = &sync.Pool{ - New: func() any { - return &[]metric.RecordOption{} - }, - } -) - -func (s HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) { - attributes := CurrentHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes) - o := metric.WithAttributeSet(attribute.NewSet(attributes...)) - recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption) - *recordOpts = append(*recordOpts, o) - s.requestBodySizeHistogram.Inst().Record(ctx, md.RequestSize, *recordOpts...) - s.responseBodySizeHistogram.Inst().Record(ctx, md.ResponseSize, *recordOpts...) - s.requestDurationHistogram.Inst().Record(ctx, md.ElapsedTime/1000.0, o) - *recordOpts = (*recordOpts)[:0] - metricRecordOptionPool.Put(recordOpts) -} - -// hasOptIn returns true if the comma-separated version string contains the -// exact optIn value. -func hasOptIn(version, optIn string) bool { - for _, v := range strings.Split(version, ",") { - if strings.TrimSpace(v) == optIn { - return true - } - } - return false -} - -func NewHTTPServer(meter metric.Meter) HTTPServer { - server := HTTPServer{} - - var err error - server.requestBodySizeHistogram, err = httpconv.NewServerRequestBodySize(meter) - handleErr(err) - - server.responseBodySizeHistogram, err = httpconv.NewServerResponseBodySize(meter) - handleErr(err) - - server.requestDurationHistogram, err = httpconv.NewServerRequestDuration( - meter, - metric.WithExplicitBucketBoundaries( - 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, - 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, - ), - ) - handleErr(err) - return server -} - -type HTTPClient struct { - requestBodySize httpconv.ClientRequestBodySize - requestDuration httpconv.ClientRequestDuration -} - -func NewHTTPClient(meter metric.Meter) HTTPClient { - client := HTTPClient{} - - var err error - client.requestBodySize, err = httpconv.NewClientRequestBodySize(meter) - handleErr(err) - - client.requestDuration, err = httpconv.NewClientRequestDuration( - meter, - metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10), - ) - handleErr(err) - - return client -} - -// RequestTraceAttrs returns attributes for an HTTP request made by a client. -func (c HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue { - return CurrentHTTPClient{}.RequestTraceAttrs(req) -} - -// ResponseTraceAttrs returns metric attributes for an HTTP request made by a client. -func (c HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue { - return CurrentHTTPClient{}.ResponseTraceAttrs(resp) -} - -func (c HTTPClient) Status(code int) (codes.Code, string) { - if code < 100 || code >= 600 { - return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) - } - if code >= 400 { - return codes.Error, "" - } - return codes.Unset, "" -} - -func (c HTTPClient) ErrorType(err error) attribute.KeyValue { - return CurrentHTTPClient{}.ErrorType(err) -} - -type MetricOpts struct { - measurement metric.MeasurementOption - addOptions metric.AddOption -} - -func (o MetricOpts) MeasurementOption() metric.MeasurementOption { - return o.measurement -} - -func (o MetricOpts) AddOptions() metric.AddOption { - return o.addOptions -} - -func (c HTTPClient) MetricOptions(ma MetricAttributes) map[string]MetricOpts { - opts := map[string]MetricOpts{} - - attributes := CurrentHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes) - set := metric.WithAttributeSet(attribute.NewSet(attributes...)) - opts["new"] = MetricOpts{ - measurement: set, - addOptions: set, - } - - return opts -} - -func (s HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts map[string]MetricOpts) { - s.requestBodySize.Inst().Record(ctx, md.RequestSize, opts["new"].MeasurementOption()) - s.requestDuration.Inst().Record(ctx, md.ElapsedTime/1000, opts["new"].MeasurementOption()) -} - -func (s HTTPClient) TraceAttributes(host string) []attribute.KeyValue { - return CurrentHTTPClient{}.TraceAttributes(host) -} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/gen.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/gen.go index 1bb207b809..a8a0d58df3 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/gen.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/gen.go @@ -6,10 +6,10 @@ package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/ // Generate semconv package: //go:generate gotmpl --body=../../../../../../internal/shared/semconv/bench_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=bench_test.go //go:generate gotmpl --body=../../../../../../internal/shared/semconv/common_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=common_test.go -//go:generate gotmpl --body=../../../../../../internal/shared/semconv/env.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=env.go -//go:generate gotmpl --body=../../../../../../internal/shared/semconv/env_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=env_test.go -//go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconv.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=httpconv.go -//go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconv_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=httpconv_test.go +//go:generate gotmpl --body=../../../../../../internal/shared/semconv/server.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=server.go +//go:generate gotmpl --body=../../../../../../internal/shared/semconv/server_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=server_test.go +//go:generate gotmpl --body=../../../../../../internal/shared/semconv/client.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=client.go +//go:generate gotmpl --body=../../../../../../internal/shared/semconv/client_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=client_test.go //go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconvtest_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=httpconvtest_test.go //go:generate gotmpl --body=../../../../../../internal/shared/semconv/util.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=util.go //go:generate gotmpl --body=../../../../../../internal/shared/semconv/util_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=util_test.go diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/httpconv.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/httpconv.go deleted file mode 100644 index 28c51a3b38..0000000000 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/httpconv.go +++ /dev/null @@ -1,517 +0,0 @@ -// Code generated by gotmpl. DO NOT MODIFY. -// source: internal/shared/semconv/httpconv.go.tmpl - -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -// Package semconv provides OpenTelemetry semantic convention types and -// functionality. -package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv" - -import ( - "fmt" - "net/http" - "reflect" - "slices" - "strconv" - "strings" - - "go.opentelemetry.io/otel/attribute" - semconvNew "go.opentelemetry.io/otel/semconv/v1.37.0" -) - -type RequestTraceAttrsOpts struct { - // If set, this is used as value for the "http.client_ip" attribute. - HTTPClientIP string -} - -type CurrentHTTPServer struct{} - -// RequestTraceAttrs returns trace attributes for an HTTP request received by a -// server. -// -// The server must be the primary server name if it is known. For example this -// would be the ServerName directive -// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache -// server, and the server_name directive -// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an -// nginx server. More generically, the primary server name would be the host -// header value that matches the default virtual host of an HTTP server. It -// should include the host identifier and if a port is used to route to the -// server that port identifier should be included as an appropriate port -// suffix. -// -// If the primary server name is not known, server should be an empty string. -// The req Host will be used to determine the server instead. -func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue { - count := 3 // ServerAddress, Method, Scheme - - var host string - var p int - if server == "" { - host, p = SplitHostPort(req.Host) - } else { - // Prioritize the primary server name. - host, p = SplitHostPort(server) - if p < 0 { - _, p = SplitHostPort(req.Host) - } - } - - hostPort := requiredHTTPPort(req.TLS != nil, p) - if hostPort > 0 { - count++ - } - - method, methodOriginal := n.method(req.Method) - if methodOriginal != (attribute.KeyValue{}) { - count++ - } - - scheme := n.scheme(req.TLS != nil) - - peer, peerPort := SplitHostPort(req.RemoteAddr) - if peer != "" { - // The Go HTTP server sets RemoteAddr to "IP:port", this will not be a - // file-path that would be interpreted with a sock family. - count++ - if peerPort > 0 { - count++ - } - } - - useragent := req.UserAgent() - if useragent != "" { - count++ - } - - // For client IP, use, in order: - // 1. The value passed in the options - // 2. The value in the X-Forwarded-For header - // 3. The peer address - clientIP := opts.HTTPClientIP - if clientIP == "" { - clientIP = serverClientIP(req.Header.Get("X-Forwarded-For")) - if clientIP == "" { - clientIP = peer - } - } - if clientIP != "" { - count++ - } - - if req.URL != nil && req.URL.Path != "" { - count++ - } - - protoName, protoVersion := netProtocol(req.Proto) - if protoName != "" && protoName != "http" { - count++ - } - if protoVersion != "" { - count++ - } - - route := httpRoute(req.Pattern) - if route != "" { - count++ - } - - attrs := make([]attribute.KeyValue, 0, count) - attrs = append(attrs, - semconvNew.ServerAddress(host), - method, - scheme, - ) - - if hostPort > 0 { - attrs = append(attrs, semconvNew.ServerPort(hostPort)) - } - if methodOriginal != (attribute.KeyValue{}) { - attrs = append(attrs, methodOriginal) - } - - if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" { - // The Go HTTP server sets RemoteAddr to "IP:port", this will not be a - // file-path that would be interpreted with a sock family. - attrs = append(attrs, semconvNew.NetworkPeerAddress(peer)) - if peerPort > 0 { - attrs = append(attrs, semconvNew.NetworkPeerPort(peerPort)) - } - } - - if useragent != "" { - attrs = append(attrs, semconvNew.UserAgentOriginal(useragent)) - } - - if clientIP != "" { - attrs = append(attrs, semconvNew.ClientAddress(clientIP)) - } - - if req.URL != nil && req.URL.Path != "" { - attrs = append(attrs, semconvNew.URLPath(req.URL.Path)) - } - - if protoName != "" && protoName != "http" { - attrs = append(attrs, semconvNew.NetworkProtocolName(protoName)) - } - if protoVersion != "" { - attrs = append(attrs, semconvNew.NetworkProtocolVersion(protoVersion)) - } - - if route != "" { - attrs = append(attrs, n.Route(route)) - } - - return attrs -} - -func (n CurrentHTTPServer) NetworkTransportAttr(network string) attribute.KeyValue { - switch network { - case "tcp", "tcp4", "tcp6": - return semconvNew.NetworkTransportTCP - case "udp", "udp4", "udp6": - return semconvNew.NetworkTransportUDP - case "unix", "unixgram", "unixpacket": - return semconvNew.NetworkTransportUnix - default: - return semconvNew.NetworkTransportPipe - } -} - -func (n CurrentHTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) { - if method == "" { - return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{} - } - if attr, ok := methodLookup[method]; ok { - return attr, attribute.KeyValue{} - } - - orig := semconvNew.HTTPRequestMethodOriginal(method) - if attr, ok := methodLookup[strings.ToUpper(method)]; ok { - return attr, orig - } - return semconvNew.HTTPRequestMethodGet, orig -} - -func (n CurrentHTTPServer) scheme(https bool) attribute.KeyValue { //nolint:revive // ignore linter - if https { - return semconvNew.URLScheme("https") - } - return semconvNew.URLScheme("http") -} - -// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP -// response. -// -// If any of the fields in the ResponseTelemetry are not set the attribute will -// be omitted. -func (n CurrentHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue { - var count int - - if resp.ReadBytes > 0 { - count++ - } - if resp.WriteBytes > 0 { - count++ - } - if resp.StatusCode > 0 { - count++ - } - - attributes := make([]attribute.KeyValue, 0, count) - - if resp.ReadBytes > 0 { - attributes = append(attributes, - semconvNew.HTTPRequestBodySize(int(resp.ReadBytes)), - ) - } - if resp.WriteBytes > 0 { - attributes = append(attributes, - semconvNew.HTTPResponseBodySize(int(resp.WriteBytes)), - ) - } - if resp.StatusCode > 0 { - attributes = append(attributes, - semconvNew.HTTPResponseStatusCode(resp.StatusCode), - ) - } - - return attributes -} - -// Route returns the attribute for the route. -func (n CurrentHTTPServer) Route(route string) attribute.KeyValue { - return semconvNew.HTTPRoute(route) -} - -func (n CurrentHTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue { - num := len(additionalAttributes) + 3 - var host string - var p int - if server == "" { - host, p = SplitHostPort(req.Host) - } else { - // Prioritize the primary server name. - host, p = SplitHostPort(server) - if p < 0 { - _, p = SplitHostPort(req.Host) - } - } - hostPort := requiredHTTPPort(req.TLS != nil, p) - if hostPort > 0 { - num++ - } - protoName, protoVersion := netProtocol(req.Proto) - if protoName != "" { - num++ - } - if protoVersion != "" { - num++ - } - - if statusCode > 0 { - num++ - } - - attributes := slices.Grow(additionalAttributes, num) - attributes = append(attributes, - semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)), - n.scheme(req.TLS != nil), - semconvNew.ServerAddress(host)) - - if hostPort > 0 { - attributes = append(attributes, semconvNew.ServerPort(hostPort)) - } - if protoName != "" { - attributes = append(attributes, semconvNew.NetworkProtocolName(protoName)) - } - if protoVersion != "" { - attributes = append(attributes, semconvNew.NetworkProtocolVersion(protoVersion)) - } - - if statusCode > 0 { - attributes = append(attributes, semconvNew.HTTPResponseStatusCode(statusCode)) - } - return attributes -} - -type CurrentHTTPClient struct{} - -// RequestTraceAttrs returns trace attributes for an HTTP request made by a client. -func (n CurrentHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue { - /* - below attributes are returned: - - http.request.method - - http.request.method.original - - url.full - - server.address - - server.port - - network.protocol.name - - network.protocol.version - */ - numOfAttributes := 3 // URL, server address, proto, and method. - - var urlHost string - if req.URL != nil { - urlHost = req.URL.Host - } - var requestHost string - var requestPort int - for _, hostport := range []string{urlHost, req.Header.Get("Host")} { - requestHost, requestPort = SplitHostPort(hostport) - if requestHost != "" || requestPort > 0 { - break - } - } - - eligiblePort := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort) - if eligiblePort > 0 { - numOfAttributes++ - } - useragent := req.UserAgent() - if useragent != "" { - numOfAttributes++ - } - - protoName, protoVersion := netProtocol(req.Proto) - if protoName != "" && protoName != "http" { - numOfAttributes++ - } - if protoVersion != "" { - numOfAttributes++ - } - - method, originalMethod := n.method(req.Method) - if originalMethod != (attribute.KeyValue{}) { - numOfAttributes++ - } - - attrs := make([]attribute.KeyValue, 0, numOfAttributes) - - attrs = append(attrs, method) - if originalMethod != (attribute.KeyValue{}) { - attrs = append(attrs, originalMethod) - } - - var u string - if req.URL != nil { - // Remove any username/password info that may be in the URL. - userinfo := req.URL.User - req.URL.User = nil - u = req.URL.String() - // Restore any username/password info that was removed. - req.URL.User = userinfo - } - attrs = append(attrs, semconvNew.URLFull(u)) - - attrs = append(attrs, semconvNew.ServerAddress(requestHost)) - if eligiblePort > 0 { - attrs = append(attrs, semconvNew.ServerPort(eligiblePort)) - } - - if protoName != "" && protoName != "http" { - attrs = append(attrs, semconvNew.NetworkProtocolName(protoName)) - } - if protoVersion != "" { - attrs = append(attrs, semconvNew.NetworkProtocolVersion(protoVersion)) - } - - return attrs -} - -// ResponseTraceAttrs returns trace attributes for an HTTP response made by a client. -func (n CurrentHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue { - /* - below attributes are returned: - - http.response.status_code - - error.type - */ - var count int - if resp.StatusCode > 0 { - count++ - } - - if isErrorStatusCode(resp.StatusCode) { - count++ - } - - attrs := make([]attribute.KeyValue, 0, count) - if resp.StatusCode > 0 { - attrs = append(attrs, semconvNew.HTTPResponseStatusCode(resp.StatusCode)) - } - - if isErrorStatusCode(resp.StatusCode) { - errorType := strconv.Itoa(resp.StatusCode) - attrs = append(attrs, semconvNew.ErrorTypeKey.String(errorType)) - } - return attrs -} - -func (n CurrentHTTPClient) ErrorType(err error) attribute.KeyValue { - t := reflect.TypeOf(err) - var value string - if t.PkgPath() == "" && t.Name() == "" { - // Likely a builtin type. - value = t.String() - } else { - value = fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()) - } - - if value == "" { - return semconvNew.ErrorTypeOther - } - - return semconvNew.ErrorTypeKey.String(value) -} - -func (n CurrentHTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) { - if method == "" { - return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{} - } - if attr, ok := methodLookup[method]; ok { - return attr, attribute.KeyValue{} - } - - orig := semconvNew.HTTPRequestMethodOriginal(method) - if attr, ok := methodLookup[strings.ToUpper(method)]; ok { - return attr, orig - } - return semconvNew.HTTPRequestMethodGet, orig -} - -func (n CurrentHTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue { - num := len(additionalAttributes) + 2 - var h string - if req.URL != nil { - h = req.URL.Host - } - var requestHost string - var requestPort int - for _, hostport := range []string{h, req.Header.Get("Host")} { - requestHost, requestPort = SplitHostPort(hostport) - if requestHost != "" || requestPort > 0 { - break - } - } - - port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort) - if port > 0 { - num++ - } - - protoName, protoVersion := netProtocol(req.Proto) - if protoName != "" { - num++ - } - if protoVersion != "" { - num++ - } - - if statusCode > 0 { - num++ - } - - attributes := slices.Grow(additionalAttributes, num) - attributes = append(attributes, - semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)), - semconvNew.ServerAddress(requestHost), - n.scheme(req), - ) - - if port > 0 { - attributes = append(attributes, semconvNew.ServerPort(port)) - } - if protoName != "" { - attributes = append(attributes, semconvNew.NetworkProtocolName(protoName)) - } - if protoVersion != "" { - attributes = append(attributes, semconvNew.NetworkProtocolVersion(protoVersion)) - } - - if statusCode > 0 { - attributes = append(attributes, semconvNew.HTTPResponseStatusCode(statusCode)) - } - return attributes -} - -// TraceAttributes returns attributes for httptrace. -func (n CurrentHTTPClient) TraceAttributes(host string) []attribute.KeyValue { - return []attribute.KeyValue{ - semconvNew.ServerAddress(host), - } -} - -func (n CurrentHTTPClient) scheme(req *http.Request) attribute.KeyValue { - if req.URL != nil && req.URL.Scheme != "" { - return semconvNew.URLScheme(req.URL.Scheme) - } - if req.TLS != nil { - return semconvNew.URLScheme("https") - } - return semconvNew.URLScheme("http") -} - -func isErrorStatusCode(code int) bool { - return code >= 400 || code < 100 -} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/server.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/server.go new file mode 100644 index 0000000000..e0e9ebc05d --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/server.go @@ -0,0 +1,403 @@ +// Code generated by gotmpl. DO NOT MODIFY. +// source: internal/shared/semconv/server.go.tmpl + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package semconv provides OpenTelemetry semantic convention types and +// functionality. +package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv" + +import ( + "context" + "fmt" + "net/http" + "slices" + "strings" + "sync" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/semconv/v1.39.0" + "go.opentelemetry.io/otel/semconv/v1.39.0/httpconv" +) + +type RequestTraceAttrsOpts struct { + // If set, this is used as value for the "http.client_ip" attribute. + HTTPClientIP string +} + +type ResponseTelemetry struct { + StatusCode int + ReadBytes int64 + ReadError error + WriteBytes int64 + WriteError error +} + +type HTTPServer struct{ + requestBodySizeHistogram httpconv.ServerRequestBodySize + responseBodySizeHistogram httpconv.ServerResponseBodySize + requestDurationHistogram httpconv.ServerRequestDuration +} + +func NewHTTPServer(meter metric.Meter) HTTPServer { + server := HTTPServer{} + + var err error + server.requestBodySizeHistogram, err = httpconv.NewServerRequestBodySize(meter) + handleErr(err) + + server.responseBodySizeHistogram, err = httpconv.NewServerResponseBodySize(meter) + handleErr(err) + + server.requestDurationHistogram, err = httpconv.NewServerRequestDuration( + meter, + metric.WithExplicitBucketBoundaries( + 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, + 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, + ), + ) + handleErr(err) + return server +} + +// Status returns a span status code and message for an HTTP status code +// value returned by a server. Status codes in the 400-499 range are not +// returned as errors. +func (n HTTPServer) Status(code int) (codes.Code, string) { + if code < 100 || code >= 600 { + return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) + } + if code >= 500 { + return codes.Error, "" + } + return codes.Unset, "" +} + +// RequestTraceAttrs returns trace attributes for an HTTP request received by a +// server. +// +// The server must be the primary server name if it is known. For example this +// would be the ServerName directive +// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache +// server, and the server_name directive +// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an +// nginx server. More generically, the primary server name would be the host +// header value that matches the default virtual host of an HTTP server. It +// should include the host identifier and if a port is used to route to the +// server that port identifier should be included as an appropriate port +// suffix. +// +// If the primary server name is not known, server should be an empty string. +// The req Host will be used to determine the server instead. +func (n HTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue { + count := 3 // ServerAddress, Method, Scheme + + var host string + var p int + if server == "" { + host, p = SplitHostPort(req.Host) + } else { + // Prioritize the primary server name. + host, p = SplitHostPort(server) + if p < 0 { + _, p = SplitHostPort(req.Host) + } + } + + hostPort := requiredHTTPPort(req.TLS != nil, p) + if hostPort > 0 { + count++ + } + + method, methodOriginal := n.method(req.Method) + if methodOriginal != (attribute.KeyValue{}) { + count++ + } + + scheme := n.scheme(req.TLS != nil) + + peer, peerPort := SplitHostPort(req.RemoteAddr) + if peer != "" { + // The Go HTTP server sets RemoteAddr to "IP:port", this will not be a + // file-path that would be interpreted with a sock family. + count++ + if peerPort > 0 { + count++ + } + } + + useragent := req.UserAgent() + if useragent != "" { + count++ + } + + // For client IP, use, in order: + // 1. The value passed in the options + // 2. The value in the X-Forwarded-For header + // 3. The peer address + clientIP := opts.HTTPClientIP + if clientIP == "" { + clientIP = serverClientIP(req.Header.Get("X-Forwarded-For")) + if clientIP == "" { + clientIP = peer + } + } + if clientIP != "" { + count++ + } + + if req.URL != nil && req.URL.Path != "" { + count++ + } + + protoName, protoVersion := netProtocol(req.Proto) + if protoName != "" && protoName != "http" { + count++ + } + if protoVersion != "" { + count++ + } + + route := httpRoute(req.Pattern) + if route != "" { + count++ + } + + attrs := make([]attribute.KeyValue, 0, count) + attrs = append(attrs, + semconv.ServerAddress(host), + method, + scheme, + ) + + if hostPort > 0 { + attrs = append(attrs, semconv.ServerPort(hostPort)) + } + if methodOriginal != (attribute.KeyValue{}) { + attrs = append(attrs, methodOriginal) + } + + if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" { + // The Go HTTP server sets RemoteAddr to "IP:port", this will not be a + // file-path that would be interpreted with a sock family. + attrs = append(attrs, semconv.NetworkPeerAddress(peer)) + if peerPort > 0 { + attrs = append(attrs, semconv.NetworkPeerPort(peerPort)) + } + } + + if useragent != "" { + attrs = append(attrs, semconv.UserAgentOriginal(useragent)) + } + + if clientIP != "" { + attrs = append(attrs, semconv.ClientAddress(clientIP)) + } + + if req.URL != nil && req.URL.Path != "" { + attrs = append(attrs, semconv.URLPath(req.URL.Path)) + } + + if protoName != "" && protoName != "http" { + attrs = append(attrs, semconv.NetworkProtocolName(protoName)) + } + if protoVersion != "" { + attrs = append(attrs, semconv.NetworkProtocolVersion(protoVersion)) + } + + if route != "" { + attrs = append(attrs, n.Route(route)) + } + + return attrs +} + +func (s HTTPServer) NetworkTransportAttr(network string) []attribute.KeyValue { + attr := semconv.NetworkTransportPipe + switch network { + case "tcp", "tcp4", "tcp6": + attr = semconv.NetworkTransportTCP + case "udp", "udp4", "udp6": + attr = semconv.NetworkTransportUDP + case "unix", "unixgram", "unixpacket": + attr = semconv.NetworkTransportUnix + } + + return []attribute.KeyValue{attr} +} + +type ServerMetricData struct { + ServerName string + ResponseSize int64 + + MetricData + MetricAttributes +} + +type MetricAttributes struct { + Req *http.Request + StatusCode int + Route string + AdditionalAttributes []attribute.KeyValue +} + +type MetricData struct { + RequestSize int64 + + // The request duration, in milliseconds + ElapsedTime float64 +} + +var ( + metricAddOptionPool = &sync.Pool{ + New: func() any { + return &[]metric.AddOption{} + }, + } + + metricRecordOptionPool = &sync.Pool{ + New: func() any { + return &[]metric.RecordOption{} + }, + } +) + +func (n HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) { + attributes := n.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.Route, md.AdditionalAttributes) + o := metric.WithAttributeSet(attribute.NewSet(attributes...)) + recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption) + *recordOpts = append(*recordOpts, o) + n.requestBodySizeHistogram.Inst().Record(ctx, md.RequestSize, *recordOpts...) + n.responseBodySizeHistogram.Inst().Record(ctx, md.ResponseSize, *recordOpts...) + n.requestDurationHistogram.Inst().Record(ctx, md.ElapsedTime/1000.0, o) + *recordOpts = (*recordOpts)[:0] + metricRecordOptionPool.Put(recordOpts) +} + +func (n HTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) { + if method == "" { + return semconv.HTTPRequestMethodGet, attribute.KeyValue{} + } + if attr, ok := methodLookup[method]; ok { + return attr, attribute.KeyValue{} + } + + orig := semconv.HTTPRequestMethodOriginal(method) + if attr, ok := methodLookup[strings.ToUpper(method)]; ok { + return attr, orig + } + return semconv.HTTPRequestMethodGet, orig +} + +func (n HTTPServer) scheme(https bool) attribute.KeyValue { //nolint:revive // ignore linter + if https { + return semconv.URLScheme("https") + } + return semconv.URLScheme("http") +} + +// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP +// response. +// +// If any of the fields in the ResponseTelemetry are not set the attribute will +// be omitted. +func (n HTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue { + var count int + + if resp.ReadBytes > 0 { + count++ + } + if resp.WriteBytes > 0 { + count++ + } + if resp.StatusCode > 0 { + count++ + } + + attributes := make([]attribute.KeyValue, 0, count) + + if resp.ReadBytes > 0 { + attributes = append(attributes, + semconv.HTTPRequestBodySize(int(resp.ReadBytes)), + ) + } + if resp.WriteBytes > 0 { + attributes = append(attributes, + semconv.HTTPResponseBodySize(int(resp.WriteBytes)), + ) + } + if resp.StatusCode > 0 { + attributes = append(attributes, + semconv.HTTPResponseStatusCode(resp.StatusCode), + ) + } + + return attributes +} + +// Route returns the attribute for the route. +func (n HTTPServer) Route(route string) attribute.KeyValue { + return semconv.HTTPRoute(route) +} + +func (n HTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, route string, additionalAttributes []attribute.KeyValue) []attribute.KeyValue { + num := len(additionalAttributes) + 3 + var host string + var p int + if server == "" { + host, p = SplitHostPort(req.Host) + } else { + // Prioritize the primary server name. + host, p = SplitHostPort(server) + if p < 0 { + _, p = SplitHostPort(req.Host) + } + } + hostPort := requiredHTTPPort(req.TLS != nil, p) + if hostPort > 0 { + num++ + } + protoName, protoVersion := netProtocol(req.Proto) + if protoName != "" { + num++ + } + if protoVersion != "" { + num++ + } + + if statusCode > 0 { + num++ + } + + if route != "" { + num++ + } + + attributes := slices.Grow(additionalAttributes, num) + attributes = append(attributes, + semconv.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)), + n.scheme(req.TLS != nil), + semconv.ServerAddress(host)) + + if hostPort > 0 { + attributes = append(attributes, semconv.ServerPort(hostPort)) + } + if protoName != "" { + attributes = append(attributes, semconv.NetworkProtocolName(protoName)) + } + if protoVersion != "" { + attributes = append(attributes, semconv.NetworkProtocolVersion(protoVersion)) + } + + if statusCode > 0 { + attributes = append(attributes, semconv.HTTPResponseStatusCode(statusCode)) + } + + if route != "" { + attributes = append(attributes, semconv.HTTPRoute(route)) + } + return attributes +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/util.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/util.go index 96422ad1ed..131fda489b 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/util.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/util.go @@ -14,7 +14,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - semconvNew "go.opentelemetry.io/otel/semconv/v1.37.0" + semconvNew "go.opentelemetry.io/otel/semconv/v1.39.0" ) // SplitHostPort splits a network address hostport of the form "host", diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go index 514ae6753b..59b6c5498d 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go @@ -5,6 +5,7 @@ package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http import ( "context" + "fmt" "io" "net/http" "net/http/httptrace" @@ -15,6 +16,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/propagation" + otelsemconv "go.opentelemetry.io/otel/semconv/v1.39.0" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request" @@ -83,6 +85,8 @@ func defaultTransportFormatter(_ string, r *http.Request) string { // RoundTrip creates a Span and propagates its context via the provided request's headers // before handing the request to the configured base RoundTripper. The created span will // end when the response body is closed or when a read from the body returns io.EOF. +// If GetBody returns an error, the error is reported via otel.Handle and the request +// continues with the original Body. func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { requestStartTime := time.Now() for _, f := range t.filters { @@ -117,11 +121,22 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { r = r.Clone(ctx) // According to RoundTripper spec, we shouldn't modify the origin request. - // if request body is nil or NoBody, we don't want to mutate the body as it + // GetBody is preferred over direct access to Body if the function is set. + // If the resulting body is nil or is NoBody, we don't want to mutate the body as it // will affect the identity of it in an unforeseeable way because we assert // ReadCloser fulfills a certain interface and it is indeed nil or NoBody. - bw := request.NewBodyWrapper(r.Body, func(int64) {}) - if r.Body != nil && r.Body != http.NoBody { + body := r.Body + if r.GetBody != nil { + b, err := r.GetBody() + if err != nil { + otel.Handle(fmt.Errorf("http.Request GetBody returned an error: %w", err)) + } else { + body = b + } + } + + bw := request.NewBodyWrapper(body, func(int64) {}) + if body != nil && body != http.NoBody { r.Body = bw } @@ -161,15 +176,7 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { }() if err != nil { - // set error type attribute if the error is part of the predefined - // error types. - // otherwise, record it as an exception - if errType := t.semconv.ErrorType(err); errType.Valid() { - span.SetAttributes(errType) - } else { - span.RecordError(err) - } - + span.SetAttributes(otelsemconv.ErrorType(err)) span.SetStatus(codes.Error, err.Error()) span.End() @@ -229,7 +236,7 @@ func (wb *wrappedBody) Write(p []byte) (int, error) { // This will not panic given the guard in newWrappedBody. n, err := wb.body.(io.Writer).Write(p) if err != nil { - wb.span.RecordError(err) + wb.span.SetAttributes(otelsemconv.ErrorType(err)) wb.span.SetStatus(codes.Error, err.Error()) } return n, err @@ -247,7 +254,7 @@ func (wb *wrappedBody) Read(b []byte) (int, error) { wb.recordBytesRead() wb.span.End() default: - wb.span.RecordError(err) + wb.span.SetAttributes(otelsemconv.ErrorType(err)) wb.span.SetStatus(codes.Error, err.Error()) } return n, err diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go index dfb53cf1f3..d0107952e1 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go @@ -4,7 +4,4 @@ package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" // Version is the current release version of the otelhttp instrumentation. -func Version() string { - return "0.63.0" - // This string is updated by the pre_release.sh script during release -} +const Version = "0.65.0" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/.codespellignore b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/.codespellignore index 2b53a25e1e..a6d0cbcc9e 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/.codespellignore +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/.codespellignore @@ -8,3 +8,4 @@ nam valu thirdparty addOpt +observ diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/.golangci.yml b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/.golangci.yml index b01762ffcc..d487228759 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/.golangci.yml +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/.golangci.yml @@ -16,6 +16,7 @@ linters: - govet - ineffassign - misspell + - modernize - perfsprint - revive - staticcheck @@ -111,6 +112,9 @@ linters: locale: US ignore-rules: - cancelled + modernize: + disable: + - omitzero perfsprint: int-conversion: true err-error: true @@ -197,6 +201,9 @@ linters: - float-compare - go-require - require-error + usetesting: + context-background: true + context-todo: true exclusions: generated: lax presets: diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/.lycheeignore b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/.lycheeignore index 5328505888..994b677df7 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/.lycheeignore +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/.lycheeignore @@ -1,4 +1,5 @@ http://localhost +https://localhost http://jaeger-collector https://github.com/open-telemetry/opentelemetry-go/milestone/ https://github.com/open-telemetry/opentelemetry-go/projects @@ -6,4 +7,7 @@ https://github.com/open-telemetry/opentelemetry-go/projects https?:\/\/github\.com\/open-telemetry\/semantic-conventions\/archive\/refs\/tags\/[^.]+\.zip\[[^]]+] file:///home/runner/work/opentelemetry-go/opentelemetry-go/libraries file:///home/runner/work/opentelemetry-go/opentelemetry-go/manual -http://4.3.2.1:78/user/123 \ No newline at end of file +http://4.3.2.1:78/user/123 +file:///home/runner/work/opentelemetry-go/opentelemetry-go/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/dns:/:4317 +# URL works, but it has blocked link checkers. +https://dl.acm.org/doi/10.1145/198429.198435 diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/CHANGELOG.md b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/CHANGELOG.md index f3abcfdc2e..e725282bec 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/CHANGELOG.md +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/CHANGELOG.md @@ -11,6 +11,111 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm +## [1.40.0/0.62.0/0.16.0] 2026-02-02 + +### Added + +- Add `AlwaysRecord` sampler in `go.opentelemetry.io/otel/sdk/trace`. (#7724) +- Add `Enabled` method to all synchronous instrument interfaces (`Float64Counter`, `Float64UpDownCounter`, `Float64Histogram`, `Float64Gauge`, `Int64Counter`, `Int64UpDownCounter`, `Int64Histogram`, `Int64Gauge`,) in `go.opentelemetry.io/otel/metric`. + This stabilizes the synchronous instrument enabled feature, allowing users to check if an instrument will process measurements before performing computationally expensive operations. (#7763) +- Add `go.opentelemetry.io/otel/semconv/v1.39.0` package. + The package contains semantic conventions from the `v1.39.0` version of the OpenTelemetry Semantic Conventions. + See the [migration documentation](./semconv/v1.39.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.38.0.` (#7783, #7789) + +### Changed + +- Improve the concurrent performance of `HistogramReservoir` in `go.opentelemetry.io/otel/sdk/metric/exemplar` by 4x. (#7443) +- Improve the concurrent performance of `FixedSizeReservoir` in `go.opentelemetry.io/otel/sdk/metric/exemplar`. (#7447) +- Improve performance of concurrent histogram measurements in `go.opentelemetry.io/otel/sdk/metric`. (#7474) +- Improve performance of concurrent synchronous gauge measurements in `go.opentelemetry.io/otel/sdk/metric`. (#7478) +- Add experimental observability metrics in `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric`. (#7492) +- `Exporter` in `go.opentelemetry.io/otel/exporters/prometheus` ignores metrics with the scope `go.opentelemetry.io/contrib/bridges/prometheus`. + This prevents scrape failures when the Prometheus exporter is misconfigured to get data from the Prometheus bridge. (#7688) +- Improve performance of concurrent exponential histogram measurements in `go.opentelemetry.io/otel/sdk/metric`. (#7702) +- The `rpc.grpc.status_code` attribute in the experimental metrics emitted from `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` is replaced with the `rpc.response.status_code` attribute to align with the semantic conventions. (#7854) +- The `rpc.grpc.status_code` attribute in the experimental metrics emitted from `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc` is replaced with the `rpc.response.status_code` attribute to align with the semantic conventions. (#7854) + +### Fixed + +- Fix bad log message when key-value pairs are dropped because of key duplication in `go.opentelemetry.io/otel/sdk/log`. (#7662) +- Fix `DroppedAttributes` on `Record` in `go.opentelemetry.io/otel/sdk/log` to not count the non-attribute key-value pairs dropped because of key duplication. (#7662) +- Fix `SetAttributes` on `Record` in `go.opentelemetry.io/otel/sdk/log` to not log that attributes are dropped when they are actually not dropped. (#7662) +- Fix missing `request.GetBody` in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` to correctly handle HTTP/2 `GOAWAY` frame. (#7794) +- `WithHostID` detector in `go.opentelemetry.io/otel/sdk/resource` to use full path for `ioreg` command on Darwin (macOS). (#7818) + +### Deprecated + +- Deprecate `go.opentelemetry.io/otel/exporters/zipkin`. + For more information, see the [OTel blog post deprecating the Zipkin exporter](https://opentelemetry.io/blog/2025/deprecating-zipkin-exporters/). (#7670) + +## [1.39.0/0.61.0/0.15.0/0.0.14] 2025-12-05 + +### Added + +- Greatly reduce the cost of recording metrics in `go.opentelemetry.io/otel/sdk/metric` using hashing for map keys. (#7175) +- Add `WithInstrumentationAttributeSet` option to `go.opentelemetry.io/otel/log`, `go.opentelemetry.io/otel/metric`, and `go.opentelemetry.io/otel/trace` packages. + This provides a concurrent-safe and performant alternative to `WithInstrumentationAttributes` by accepting a pre-constructed `attribute.Set`. (#7287) +- Add experimental observability for the Prometheus exporter in `go.opentelemetry.io/otel/exporters/prometheus`. + Check the `go.opentelemetry.io/otel/exporters/prometheus/internal/x` package documentation for more information. (#7345) +- Add experimental observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#7353) +- Add temporality selector functions `DeltaTemporalitySelector`, `CumulativeTemporalitySelector`, `LowMemoryTemporalitySelector` to `go.opentelemetry.io/otel/sdk/metric`. (#7434) +- Add experimental observability metrics for simple log processor in `go.opentelemetry.io/otel/sdk/log`. (#7548) +- Add experimental observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#7459) +- Add experimental observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#7486) +- Add experimental observability metrics for simple span processor in `go.opentelemetry.io/otel/sdk/trace`. (#7374) +- Add experimental observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#7512) +- Add experimental observability metrics for manual reader in `go.opentelemetry.io/otel/sdk/metric`. (#7524) +- Add experimental observability metrics for periodic reader in `go.opentelemetry.io/otel/sdk/metric`. (#7571) +- Support `OTEL_EXPORTER_OTLP_LOGS_INSECURE` and `OTEL_EXPORTER_OTLP_INSECURE` environmental variables in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#7608) +- Add `Enabled` method to the `Processor` interface in `go.opentelemetry.io/otel/sdk/log`. + All `Processor` implementations now include an `Enabled` method. (#7639) +- The `go.opentelemetry.io/otel/semconv/v1.38.0` package. + The package contains semantic conventions from the `v1.38.0` version of the OpenTelemetry Semantic Conventions. + See the [migration documentation](./semconv/v1.38.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.37.0.`(#7648) + +### Changed + +- `Distinct` in `go.opentelemetry.io/otel/attribute` is no longer guaranteed to uniquely identify an attribute set. + Collisions between `Distinct` values for different Sets are possible with extremely high cardinality (billions of series per instrument), but are highly unlikely. (#7175) +- `WithInstrumentationAttributes` in `go.opentelemetry.io/otel/trace` synchronously de-duplicates the passed attributes instead of delegating it to the returned `TracerOption`. (#7266) +- `WithInstrumentationAttributes` in `go.opentelemetry.io/otel/meter` synchronously de-duplicates the passed attributes instead of delegating it to the returned `MeterOption`. (#7266) +- `WithInstrumentationAttributes` in `go.opentelemetry.io/otel/log` synchronously de-duplicates the passed attributes instead of delegating it to the returned `LoggerOption`. (#7266) +- Rename the `OTEL_GO_X_SELF_OBSERVABILITY` environment variable to `OTEL_GO_X_OBSERVABILITY` in `go.opentelemetry.io/otel/sdk/trace`, `go.opentelemetry.io/otel/sdk/log`, and `go.opentelemetry.io/otel/exporters/stdout/stdouttrace`. (#7302) +- Improve performance of histogram `Record` in `go.opentelemetry.io/otel/sdk/metric` when min and max are disabled using `NoMinMax`. (#7306) +- Improve error handling for dropped data during translation by using `prometheus.NewInvalidMetric` in `go.opentelemetry.io/otel/exporters/prometheus`. + ⚠️ **Breaking Change:** Previously, these cases were only logged and scrapes succeeded. + Now, when translation would drop data (e.g., invalid label/value), the exporter emits a `NewInvalidMetric`, and Prometheus scrapes **fail with HTTP 500** by default. + To preserve the prior behavior (scrapes succeed while errors are logged), configure your Prometheus HTTP handler with: `promhttp.HandlerOpts{ ErrorHandling: promhttp.ContinueOnError }`. (#7363) +- Replace fnv hash with xxhash in `go.opentelemetry.io/otel/attribute` for better performance. (#7371) +- The default `TranslationStrategy` in `go.opentelemetry.io/exporters/prometheus` is changed from `otlptranslator.NoUTF8EscapingWithSuffixes` to `otlptranslator.UnderscoreEscapingWithSuffixes`. (#7421) +- Improve performance of concurrent measurements in `go.opentelemetry.io/otel/sdk/metric`. (#7427) +- Include W3C TraceFlags (bits 0–7) in the OTLP `Span.Flags` field in `go.opentelemetry.io/exporters/otlp/otlptrace/otlptracehttp` and `go.opentelemetry.io/exporters/otlp/otlptrace/otlptracegrpc`. (#7438) +- The `ErrorType` function in `go.opentelemetry.io/otel/semconv/v1.37.0` now handles custom error types. + If an error implements an `ErrorType() string` method, the return value of that method will be used as the error type. (#7442) + +### Fixed + +- Fix `WithInstrumentationAttributes` options in `go.opentelemetry.io/otel/trace`, `go.opentelemetry.io/otel/metric`, and `go.opentelemetry.io/otel/log` to properly merge attributes when passed multiple times instead of replacing them. + Attributes with duplicate keys will use the last value passed. (#7300) +- The equality of `attribute.Set` when using the `Equal` method is not affected by the user overriding the empty set pointed to by `attribute.EmptySet` in `go.opentelemetry.io/otel/attribute`. (#7357) +- Return partial OTLP export errors to the caller in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#7372) +- Return partial OTLP export errors to the caller in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#7372) +- Return partial OTLP export errors to the caller in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#7372) +- Return partial OTLP export errors to the caller in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#7372) +- Return partial OTLP export errors to the caller in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#7372) +- Return partial OTLP export errors to the caller in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#7372) +- Fix `AddAttributes`, `SetAttributes`, `SetBody` on `Record` in `go.opentelemetry.io/otel/sdk/log` to not mutate input. (#7403) +- Do not double record measurements of `RecordSet` methods in `go.opentelemetry.io/otel/semconv/v1.37.0`. (#7655) +- Do not double record measurements of `RecordSet` methods in `go.opentelemetry.io/otel/semconv/v1.36.0`. (#7656) + +### Removed + +- Drop support for [Go 1.23]. (#7274) +- Remove the `FilterProcessor` interface in `go.opentelemetry.io/otel/sdk/log`. + The `Enabled` method has been added to the `Processor` interface instead. + All `Processor` implementations must now implement the `Enabled` method. + Custom processors that do not filter records can implement `Enabled` to return `true`. (#7639) + ## [1.38.0/0.60.0/0.14.0/0.0.13] 2025-08-29 This release is the last to support [Go 1.23]. @@ -3430,8 +3535,11 @@ It contains api and sdk for trace and meter. - CircleCI build CI manifest files. - CODEOWNERS file to track owners of this project. -[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.38.0...HEAD +[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.40.0...HEAD +[1.40.0/0.62.0/0.16.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.40.0 +[1.39.0/0.61.0/0.15.0/0.0.14]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.39.0 [1.38.0/0.60.0/0.14.0/0.0.13]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.38.0 +[0.59.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/exporters/prometheus/v0.59.1 [1.37.0/0.59.0/0.13.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.37.0 [0.12.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/log/v0.12.2 [0.12.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/log/v0.12.1 diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md index 0b3ae855c1..38dede9329 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md @@ -54,8 +54,8 @@ go get -d go.opentelemetry.io/otel (This may print some warning about "build constraints exclude all Go files", just ignore it.) -This will put the project in `${GOPATH}/src/go.opentelemetry.io/otel`. You -can alternatively use `git` directly with: +This will put the project in `${GOPATH}/src/go.opentelemetry.io/otel`. +Alternatively, you can use `git` directly with: ```sh git clone https://github.com/open-telemetry/opentelemetry-go @@ -65,8 +65,7 @@ git clone https://github.com/open-telemetry/opentelemetry-go that name is a kind of a redirector to GitHub that `go get` can understand, but `git` does not.) -This would put the project in the `opentelemetry-go` directory in -current working directory. +This will add the project as `opentelemetry-go` within the current directory. Enter the newly created directory and add your fork as a new remote: @@ -109,7 +108,7 @@ A PR is considered **ready to merge** when: This is not enforced through automation, but needs to be validated by the maintainer merging. - * At least one of the qualified approvals need to be from an + * At least one of the qualified approvals needs to be from an [Approver]/[Maintainer] affiliated with a different company than the author of the PR. * PRs introducing changes that have already been discussed and consensus @@ -166,11 +165,11 @@ guidelines](https://opentelemetry.io/docs/specs/otel/library-guidelines). ### Focus on Capabilities, Not Structure Compliance OpenTelemetry is an evolving specification, one where the desires and -use cases are clear, but the method to satisfy those uses cases are +use cases are clear, but the methods to satisfy those use cases are not. As such, Contributions should provide functionality and behavior that -conforms to the specification, but the interface and structure is +conforms to the specification, but the interface and structure are flexible. It is preferable to have contributions follow the idioms of the @@ -217,7 +216,7 @@ about dependency compatibility. This project does not partition dependencies based on the environment (i.e. `development`, `staging`, `production`). -Only the dependencies explicitly included in the released modules have be +Only the dependencies explicitly included in the released modules have been tested and verified to work with the released code. No other guarantee is made about the compatibility of other dependencies. @@ -635,8 +634,8 @@ is not in their root name. The use of internal packages should be scoped to a single module. A sub-module should never import from a parent internal package. This creates a coupling -between the two modules where a user can upgrade the parent without the child -and if the internal package API has changed it will fail to upgrade[^3]. +between the two modules where a user can upgrade the parent without the child, +and if the internal package API has changed, it will fail to upgrade[^3]. There are two known exceptions to this rule: @@ -657,7 +656,7 @@ this. ### Ignoring context cancellation -OpenTelemetry API implementations need to ignore the cancellation of the context that are +OpenTelemetry API implementations need to ignore the cancellation of the context that is passed when recording a value (e.g. starting a span, recording a measurement, emitting a log). Recording methods should not return an error describing the cancellation state of the context when they complete, nor should they abort any work. @@ -675,6 +674,441 @@ force flushing telemetry, shutting down a signal provider) the context cancellat should be honored. This means all work done on behalf of the user provided context should be canceled. +### Observability + +OpenTelemetry Go SDK components should be instrumented to enable users observability for the health and performance of the telemetry pipeline itself. +This allows operators to understand how well their observability infrastructure is functioning and to identify potential issues before they impact their applications. + +This section outlines the best practices for building instrumentation in OpenTelemetry Go SDK components. + +#### Environment Variable Activation + +Observability features are currently experimental. +They should be disabled by default and activated through the `OTEL_GO_X_OBSERVABILITY` environment variable. +This follows the established experimental feature pattern used throughout the SDK. + +Components should check for this environment variable using a consistent pattern: + +```go +import "go.opentelemetry.io/otel/*/internal/x" + +if x.Observability.Enabled() { + // Initialize observability metrics +} +``` + +**References**: + +- [stdouttrace exporter](./exporters/stdout/stdouttrace/internal/x/x.go) +- [sdk](./sdk/internal/x/x.go) + +#### Encapsulation + +Instrumentation should be encapsulated within a dedicated `struct` (e.g. `instrumentation`). +It should not be mixed into the instrumented component. + +Prefer this: + +```go +type SDKComponent struct { + inst *instrumentation +} + +type instrumentation struct { + inflight otelconv.SDKComponentInflight + exported otelconv.SDKComponentExported +} +``` + +To this: + +```go +// ❌ Avoid this pattern. +type SDKComponent struct { + /* other SDKComponent fields... */ + + inflight otelconv.SDKComponentInflight + exported otelconv.SDKComponentExported +} +``` + +The instrumentation code should not bloat the code being instrumented. +Likely, this means its own file, or its own package if it is complex or reused. + +#### Initialization + +Instrumentation setup should be explicit, side-effect free, and local to the relevant component. +Avoid relying on global or implicit [side effects][side-effect] for initialization. + +Encapsulate setup in constructor functions, ensuring clear ownership and scope: + +```go +import ( + "errors" + + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" + "go.opentelemetry.io/otel/semconv/v1.39.0/otelconv" +) + +type SDKComponent struct { + inst *instrumentation +} + +func NewSDKComponent(config Config) (*SDKComponent, error) { + inst, err := newInstrumentation() + if err != nil { + return nil, err + } + return &SDKComponent{inst: inst}, nil +} + +type instrumentation struct { + inflight otelconv.SDKComponentInflight + exported otelconv.SDKComponentExported +} + +func newInstrumentation() (*instrumentation, error) { + if !x.Observability.Enabled() { + return nil, nil + } + + meter := otel.GetMeterProvider().Meter( + "", + metric.WithInstrumentationVersion(sdk.Version()), + metric.WithSchemaURL(semconv.SchemaURL), + ) + + inst := &instrumentation{} + + var err, e error + inst.inflight, e = otelconv.NewSDKComponentInflight(meter) + err = errors.Join(err, e) + + inst.exported, e = otelconv.NewSDKComponentExported(meter) + err = errors.Join(err, e) + + return inst, err +} +``` + +```go +// ❌ Avoid this pattern. +func (c *Component) initObservability() { + // Initialize observability metrics + if !x.Observability.Enabled() { + return + } + + // Initialize observability metrics + c.inst = &instrumentation{/* ... */} +} +``` + +[side-effect]: https://en.wikipedia.org/wiki/Side_effect_(computer_science) + +#### Performance + +When observability is disabled there should be little to no overhead. + +```go +func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { + if e.inst != nil { + attrs := expensiveOperation() + e.inst.recordSpanInflight(ctx, int64(len(spans)), attrs...) + } + // Export spans... +} +``` + +```go +// ❌ Avoid this pattern. +func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { + attrs := expensiveOperation() + e.inst.recordSpanInflight(ctx, int64(len(spans)), attrs...) + // Export spans... +} + +func (i *instrumentation) recordSpanInflight(ctx context.Context, count int64, attrs ...attribute.KeyValue) { + if i == nil || i.inflight == nil { + return + } + i.inflight.Add(ctx, count, metric.WithAttributes(attrs...)) +} +``` + +When observability is enabled, the instrumentation code paths should be optimized to reduce allocation and computation overhead. + +##### Attribute and Option Allocation Management + +Pool attribute slices and options with [`sync.Pool`] to minimize allocations in measurement calls with dynamic attributes. + +```go +var ( + attrPool = sync.Pool{ + New: func() any { + // Pre-allocate common capacity + knownCap := 8 // Adjust based on expected usage + s := make([]attribute.KeyValue, 0, knownCap) + // Return a pointer to avoid extra allocation on Put(). + return &s + }, + } + + addOptPool = &sync.Pool{ + New: func() any { + const n = 1 // WithAttributeSet + o := make([]metric.AddOption, 0, n) + // Return a pointer to avoid extra allocation on Put(). + return &o + }, + } +) + +func (i *instrumentation) record(ctx context.Context, value int64, baseAttrs ...attribute.KeyValue) { + attrs := attrPool.Get().(*[]attribute.KeyValue) + defer func() { + *attrs = (*attrs)[:0] // Reset. + attrPool.Put(attrs) + }() + + *attrs = append(*attrs, baseAttrs...) + // Add any dynamic attributes. + *attrs = append(*attrs, semconv.OTelComponentName("exporter-1")) + + addOpt := addOptPool.Get().(*[]metric.AddOption) + defer func() { + *addOpt = (*addOpt)[:0] + addOptPool.Put(addOpt) + }() + + set := attribute.NewSet(*attrs...) + *addOpt = append(*addOpt, metric.WithAttributeSet(set)) + + i.counter.Add(ctx, value, *addOpt...) +} +``` + +Pools are most effective when there are many pooled objects of the same sufficiently large size, and the objects are repeatedly used. +This amortizes the cost of allocation and synchronization. +Ideally, the pools should be scoped to be used as widely as possible within the component to maximize this efficiency while still ensuring correctness. + +[`sync.Pool`]: https://pkg.go.dev/sync#Pool + +##### Cache common attribute sets for repeated measurements + +If a static set of attributes are used for measurements and they are known at compile time, pre-compute and cache these attributes. + +```go +type spanLiveSetKey struct { + sampled bool +} + +var spanLiveSetCache = map[spanLiveSetKey]attribute.Set{ + {true}: attribute.NewSet( + otelconv.SDKSpanLive{}.AttrSpanSamplingResult( + otelconv.SpanSamplingResultRecordAndSample, + ), + ), + {false}: attribute.NewSet( + otelconv.SDKSpanLive{}.AttrSpanSamplingResult( + otelconv.SpanSamplingResultRecordOnly, + ), + ), +} + +func spanLiveSet(sampled bool) attribute.Set { + key := spanLiveSetKey{sampled: sampled} + return spanLiveSetCache[key] +} +``` + +##### Benchmarking + +Always provide benchmarks when introducing or refactoring instrumentation. +Demonstrate the impact (allocs/op, B/op, ns/op) in enabled/disabled scenarios: + +```go +func BenchmarkExportSpans(b *testing.B) { + scenarios := []struct { + name string + obsEnabled bool + }{ + {"ObsDisabled", false}, + {"ObsEnabled", true}, + } + + for _, scenario := range scenarios { + b.Run(scenario.name, func(b *testing.B) { + b.Setenv( + "OTEL_GO_X_OBSERVABILITY", + strconv.FormatBool(scenario.obsEnabled), + ) + + exporter := NewExporter() + spans := generateTestSpans(100) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + _ = exporter.ExportSpans(context.Background(), spans) + } + }) + } +} +``` + +#### Error Handling and Robustness + +Errors should be reported back to the caller if possible, and partial failures should be handled as gracefully as possible. + +```go +func newInstrumentation() (*instrumentation, error) { + if !x.Observability.Enabled() { + return nil, nil + } + + m := otel.GetMeterProvider().Meter(/* initialize meter */) + counter, err := otelconv.NewSDKComponentCounter(m) + // Use the partially initialized counter if available. + i := &instrumentation{counter: counter} + // Return any error to the caller. + return i, err +} +``` + +```go +// ❌ Avoid this pattern. +func newInstrumentation() *instrumentation { + if !x.Observability.Enabled() { + return nil, nil + } + + m := otel.GetMeterProvider().Meter(/* initialize meter */) + counter, err := otelconv.NewSDKComponentCounter(m) + if err != nil { + // ❌ Do not dump the error to the OTel Handler. Return it to the + // caller. + otel.Handle(err) + // ❌ Do not return nil if we can still use the partially initialized + // counter. + return nil + } + return &instrumentation{counter: counter} +} +``` + +If the instrumented component cannot report the error to the user, let it report the error to `otel.Handle`. + +#### Context Propagation + +Ensure observability measurements receive the correct context, especially for trace exemplars and distributed context: + +```go +func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { + // Use the provided context for observability measurements + e.inst.recordSpanExportStarted(ctx, len(spans)) + + err := e.doExport(ctx, spans) + + if err != nil { + e.inst.recordSpanExportFailed(ctx, len(spans), err) + } else { + e.inst.recordSpanExportSucceeded(ctx, len(spans)) + } + + return err +} +``` + +```go +// ❌ Avoid this pattern. +func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { + // ❌ Do not break the context propagation. + e.inst.recordSpanExportStarted(context.Background(), len(spans)) + + err := e.doExport(ctx, spans) + + /* ... */ + + return err +} +``` + +#### Semantic Conventions Compliance + +All observability metrics should follow the [OpenTelemetry Semantic Conventions for SDK metrics](https://github.com/open-telemetry/semantic-conventions/blob/1cf2476ae5e518225a766990a28a6d5602bd5a30/docs/otel/sdk-metrics.md). + +Use the metric semantic conventions convenience package [otelconv](./semconv/v1.39.0/otelconv/metric.go). + +##### Component Identification + +Component names and types should follow [semantic convention](https://github.com/open-telemetry/semantic-conventions/blob/1cf2476ae5e518225a766990a28a6d5602bd5a30/docs/registry/attributes/otel.md#otel-component-attributes). + +If a component is not a well-known type specified in the semantic conventions, use the package path scope type as a stable identifier. + +```go +componentType := "go.opentelemetry.io/otel/sdk/trace.Span" +``` + +```go +// ❌ Do not do this. +componentType := "trace-span" +``` + +The component name should be a stable unique identifier for the specific instance of the component. + +Use a global counter to ensure uniqueness if necessary. + +```go +// Unique 0-based ID counter for component instances. +var componentIDCounter atomic.Int64 + +// nextID returns the next unique ID for a component. +func nextID() int64 { + return componentIDCounter.Add(1) - 1 +} + +// componentName returns a unique name for the component instance. +func componentName() attribute.KeyValue { + id := nextID() + name := fmt.Sprintf("%s/%d", componentType, id) + return semconv.OTelComponentName(name) +} +``` + +The component ID will need to be resettable for deterministic testing. +If tests are in a different package than the component being tested (i.e. a `_test` package name), use a generated `counter` internal package to manage the counter. +See [stdouttrace exporter example](./exporters/stdout/stdouttrace/internal/gen.go) for reference. + +#### Testing + +Use deterministic testing with isolated state: + +```go +func TestObservability(t *testing.T) { + // Restore state after test to ensure this does not affect other tests. + prev := otel.GetMeterProvider() + t.Cleanup(func() { otel.SetMeterProvider(prev) }) + + // Isolate the meter provider for deterministic testing + reader := metric.NewManualReader() + meterProvider := metric.NewMeterProvider(metric.WithReader(reader)) + otel.SetMeterProvider(meterProvider) + + // Use t.Setenv to ensure environment variable is restored after test. + t.Setenv("OTEL_GO_X_OBSERVABILITY", "true") + + // Reset component ID counter to ensure deterministic component names. + componentIDCounter.Store(0) + + /* ... test code ... */ +} +``` + +Test order should not affect results. +Ensure that any global state (e.g. component ID counters) is reset between tests. + ## Approvers and Maintainers ### Maintainers @@ -696,7 +1130,6 @@ For more information about the approver role, see the [community repository](htt ### Triagers - [Alex Kats](https://github.com/akats7), Capital One -- [Cheng-Zhen Yang](https://github.com/scorpionknifes), Independent For more information about the triager role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#triager). @@ -704,6 +1137,7 @@ For more information about the triager role, see the [community repository](http - [Aaron Clawson](https://github.com/MadVikingGod) - [Anthony Mirabella](https://github.com/Aneurysm9) +- [Cheng-Zhen Yang](https://github.com/scorpionknifes) - [Chester Cheung](https://github.com/hanyuancheung) - [Evan Torrie](https://github.com/evantorrie) - [Gustavo Silva Paiva](https://github.com/paivagustavo) diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/Makefile b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/Makefile index bc0f1f92d1..44870248c3 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/Makefile +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/Makefile @@ -146,11 +146,12 @@ build-tests/%: # Tests -TEST_TARGETS := test-default test-bench test-short test-verbose test-race test-concurrent-safe +TEST_TARGETS := test-default test-bench test-short test-verbose test-race test-concurrent-safe test-fuzz .PHONY: $(TEST_TARGETS) test test-default test-race: ARGS=-race test-bench: ARGS=-run=xxxxxMatchNothingxxxxx -test.benchtime=1ms -bench=. test-short: ARGS=-short +test-fuzz: ARGS=-fuzztime=10s -fuzz test-verbose: ARGS=-v -race test-concurrent-safe: ARGS=-run=ConcurrentSafe -count=100 -race test-concurrent-safe: TIMEOUT=120 diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/README.md b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/README.md index 6b7ab5f219..c633595431 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/README.md +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/README.md @@ -55,25 +55,18 @@ Currently, this project supports the following environments. |----------|------------|--------------| | Ubuntu | 1.25 | amd64 | | Ubuntu | 1.24 | amd64 | -| Ubuntu | 1.23 | amd64 | | Ubuntu | 1.25 | 386 | | Ubuntu | 1.24 | 386 | -| Ubuntu | 1.23 | 386 | | Ubuntu | 1.25 | arm64 | | Ubuntu | 1.24 | arm64 | -| Ubuntu | 1.23 | arm64 | -| macOS 13 | 1.25 | amd64 | -| macOS 13 | 1.24 | amd64 | -| macOS 13 | 1.23 | amd64 | +| macOS | 1.25 | amd64 | +| macOS | 1.24 | amd64 | | macOS | 1.25 | arm64 | | macOS | 1.24 | arm64 | -| macOS | 1.23 | arm64 | | Windows | 1.25 | amd64 | | Windows | 1.24 | amd64 | -| Windows | 1.23 | amd64 | | Windows | 1.25 | 386 | | Windows | 1.24 | 386 | -| Windows | 1.23 | 386 | While this project should work for other systems, no compatibility guarantees are made for those systems currently. diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/RELEASING.md b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/RELEASING.md index 1ddcdef039..861756fd74 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/RELEASING.md +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/RELEASING.md @@ -24,7 +24,7 @@ Ensure things look correct before submitting a pull request to include the addit ## Breaking changes validation -You can run `make gorelease` that runs [gorelease](https://pkg.go.dev/golang.org/x/exp/cmd/gorelease) to ensure that there are no unwanted changes done in the public API. +You can run `make gorelease` which runs [gorelease](https://pkg.go.dev/golang.org/x/exp/cmd/gorelease) to ensure that there are no unwanted changes made in the public API. You can check/report problems with `gorelease` [here](https://golang.org/issues/26420). @@ -62,7 +62,7 @@ Update go.mod for submodules to depend on the new release which will happen in t ``` 3. Update the [Changelog](./CHANGELOG.md). - - Make sure all relevant changes for this release are included and are in language that non-contributors to the project can understand. + - Make sure all relevant changes for this release are included and are written in language that non-contributors to the project can understand. To verify this, you can look directly at the commits since the ``. ``` @@ -107,34 +107,50 @@ It is critical you make sure the version you push upstream is correct. ... ``` -## Release +## Sign artifacts -Finally create a Release for the new `` on GitHub. -The release body should include all the release notes from the Changelog for this release. +To ensure we comply with CNCF best practices, we need to sign the release artifacts. -### Sign the Release Artifact +Download the `.tar.gz` and `.zip` archives from the [tags page](https://github.com/open-telemetry/opentelemetry-go/tags) for the new release tag. +Both archives need to be signed with your GPG key. -To ensure we comply with CNCF best practices, we need to sign the release artifact. -The tarball attached to the GitHub release needs to be signed with your GPG key. +You can use [this script] to verify the contents of the archives before signing them. -Follow [these steps] to sign the release artifact and upload it to GitHub. -You can use [this script] to verify the contents of the tarball before signing it. +To find your GPG key ID, run: -Be sure to use the correct GPG key when signing the release artifact. +```terminal +gpg --list-secret-keys --keyid-format=long +``` + +The key ID is the 16-character string after `sec rsa4096/` (or similar). + +Set environment variables and sign both artifacts: ```terminal -gpg --local-user --armor --detach-sign opentelemetry-go-.tar.gz +export VERSION="" # e.g., v1.32.0 +export KEY_ID="" + +gpg --local-user $KEY_ID --armor --detach-sign opentelemetry-go-$VERSION.tar.gz +gpg --local-user $KEY_ID --armor --detach-sign opentelemetry-go-$VERSION.zip ``` -You can verify the signature with: +You can verify the signatures with: ```terminal -gpg --verify opentelemetry-go-.tar.gz.asc opentelemetry-go-.tar.gz +gpg --verify opentelemetry-go-$VERSION.tar.gz.asc opentelemetry-go-$VERSION.tar.gz +gpg --verify opentelemetry-go-$VERSION.zip.asc opentelemetry-go-$VERSION.zip ``` -[these steps]: https://wiki.debian.org/Creating%20signed%20GitHub%20releases [this script]: https://github.com/MrAlias/attest-sh +## Release + +Finally create a Release for the new `` on GitHub. +The release body should include all the release notes from the Changelog for this release. + +***IMPORTANT***: GitHub Releases are immutable once created. +You must upload the signed artifacts (`.tar.gz`, `.tar.gz.asc`, `.zip`, and `.zip.asc`) when creating the release, as they cannot be added or modified later. + ## Post-Release ### Contrib Repository @@ -160,14 +176,6 @@ This helps track what changes were included in each release. Once all related issues and PRs have been added to the milestone, close the milestone. -### Demo Repository - -Bump the dependencies in the following Go services: - -- [`accounting`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/accounting) -- [`checkoutservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/checkout) -- [`productcatalogservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/product-catalog) - ### Close the `Version Release` issue Once the todo list in the `Version Release` issue is complete, close the issue. diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/VERSIONING.md b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/VERSIONING.md index b8cb605c16..b27c9e84f5 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/VERSIONING.md +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/VERSIONING.md @@ -83,7 +83,7 @@ is designed so the following goals can be achieved. in either the module path or the import path. * In addition to public APIs, telemetry produced by stable instrumentation will remain stable and backwards compatible. This is to avoid breaking - alerts and dashboard. + alerts and dashboards. * Modules will be used to encapsulate instrumentation, detectors, exporters, propagators, and any other independent sets of related components. * Experimental modules still under active development will be versioned at diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/encoder.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/encoder.go index 6333d34b31..6cc1a1655c 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/encoder.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/encoder.go @@ -16,7 +16,7 @@ type ( // set into a wire representation. Encoder interface { // Encode returns the serialized encoding of the attribute set using - // its Iterator. This result may be cached by a attribute.Set. + // its Iterator. This result may be cached by an attribute.Set. Encode(iterator Iterator) string // ID returns a value that is unique for each class of attribute diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/hash.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/hash.go new file mode 100644 index 0000000000..6aa69aeaec --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/hash.go @@ -0,0 +1,92 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package attribute // import "go.opentelemetry.io/otel/attribute" + +import ( + "fmt" + "reflect" + + "go.opentelemetry.io/otel/attribute/internal/xxhash" +) + +// Type identifiers. These identifiers are hashed before the value of the +// corresponding type. This is done to distinguish values that are hashed with +// the same value representation (e.g. `int64(1)` and `true`, []int64{0} and +// int64(0)). +// +// These are all 8 byte length strings converted to a uint64 representation. A +// uint64 is used instead of the string directly as an optimization, it avoids +// the for loop in [xxhash] which adds minor overhead. +const ( + boolID uint64 = 7953749933313450591 // "_boolean" (little endian) + int64ID uint64 = 7592915492740740150 // "64_bit_i" (little endian) + float64ID uint64 = 7376742710626956342 // "64_bit_f" (little endian) + stringID uint64 = 6874584755375207263 // "_string_" (little endian) + boolSliceID uint64 = 6875993255270243167 // "_[]bool_" (little endian) + int64SliceID uint64 = 3762322556277578591 // "_[]int64" (little endian) + float64SliceID uint64 = 7308324551835016539 // "[]double" (little endian) + stringSliceID uint64 = 7453010373645655387 // "[]string" (little endian) +) + +// hashKVs returns a new xxHash64 hash of kvs. +func hashKVs(kvs []KeyValue) uint64 { + h := xxhash.New() + for _, kv := range kvs { + h = hashKV(h, kv) + } + return h.Sum64() +} + +// hashKV returns the xxHash64 hash of kv with h as the base. +func hashKV(h xxhash.Hash, kv KeyValue) xxhash.Hash { + h = h.String(string(kv.Key)) + + switch kv.Value.Type() { + case BOOL: + h = h.Uint64(boolID) + h = h.Uint64(kv.Value.numeric) + case INT64: + h = h.Uint64(int64ID) + h = h.Uint64(kv.Value.numeric) + case FLOAT64: + h = h.Uint64(float64ID) + // Assumes numeric stored with math.Float64bits. + h = h.Uint64(kv.Value.numeric) + case STRING: + h = h.Uint64(stringID) + h = h.String(kv.Value.stringly) + case BOOLSLICE: + h = h.Uint64(boolSliceID) + rv := reflect.ValueOf(kv.Value.slice) + for i := 0; i < rv.Len(); i++ { + h = h.Bool(rv.Index(i).Bool()) + } + case INT64SLICE: + h = h.Uint64(int64SliceID) + rv := reflect.ValueOf(kv.Value.slice) + for i := 0; i < rv.Len(); i++ { + h = h.Int64(rv.Index(i).Int()) + } + case FLOAT64SLICE: + h = h.Uint64(float64SliceID) + rv := reflect.ValueOf(kv.Value.slice) + for i := 0; i < rv.Len(); i++ { + h = h.Float64(rv.Index(i).Float()) + } + case STRINGSLICE: + h = h.Uint64(stringSliceID) + rv := reflect.ValueOf(kv.Value.slice) + for i := 0; i < rv.Len(); i++ { + h = h.String(rv.Index(i).String()) + } + case INVALID: + default: + // Logging is an alternative, but using the internal logger here + // causes an import cycle so it is not done. + v := kv.Value.AsInterface() + msg := fmt.Sprintf("unknown value type: %[1]v (%[1]T)", v) + panic(msg) + } + return h +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/internal/attribute.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/internal/attribute.go index 0875504302..7f5eae877d 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/internal/attribute.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/internal/attribute.go @@ -13,32 +13,28 @@ import ( // BoolSliceValue converts a bool slice into an array with same elements as slice. func BoolSliceValue(v []bool) any { - var zero bool - cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem() + cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[bool]())).Elem() reflect.Copy(cp, reflect.ValueOf(v)) return cp.Interface() } // Int64SliceValue converts an int64 slice into an array with same elements as slice. func Int64SliceValue(v []int64) any { - var zero int64 - cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem() + cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[int64]())).Elem() reflect.Copy(cp, reflect.ValueOf(v)) return cp.Interface() } // Float64SliceValue converts a float64 slice into an array with same elements as slice. func Float64SliceValue(v []float64) any { - var zero float64 - cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem() + cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[float64]())).Elem() reflect.Copy(cp, reflect.ValueOf(v)) return cp.Interface() } // StringSliceValue converts a string slice into an array with same elements as slice. func StringSliceValue(v []string) any { - var zero string - cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem() + cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[string]())).Elem() reflect.Copy(cp, reflect.ValueOf(v)) return cp.Interface() } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/internal/xxhash/xxhash.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/internal/xxhash/xxhash.go new file mode 100644 index 0000000000..113a978383 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/internal/xxhash/xxhash.go @@ -0,0 +1,64 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package xxhash provides a wrapper around the xxhash library for attribute hashing. +package xxhash // import "go.opentelemetry.io/otel/attribute/internal/xxhash" + +import ( + "encoding/binary" + "math" + + "github.com/cespare/xxhash/v2" +) + +// Hash wraps xxhash.Digest to provide an API friendly for hashing attribute values. +type Hash struct { + d *xxhash.Digest +} + +// New returns a new initialized xxHash64 hasher. +func New() Hash { + return Hash{d: xxhash.New()} +} + +func (h Hash) Uint64(val uint64) Hash { + var buf [8]byte + binary.LittleEndian.PutUint64(buf[:], val) + // errors from Write are always nil for xxhash + // if it returns an err then panic + _, err := h.d.Write(buf[:]) + if err != nil { + panic("xxhash write of uint64 failed: " + err.Error()) + } + return h +} + +func (h Hash) Bool(val bool) Hash { // nolint:revive // This is a hashing function. + if val { + return h.Uint64(1) + } + return h.Uint64(0) +} + +func (h Hash) Float64(val float64) Hash { + return h.Uint64(math.Float64bits(val)) +} + +func (h Hash) Int64(val int64) Hash { + return h.Uint64(uint64(val)) // nolint:gosec // Overflow doesn't matter since we are hashing. +} + +func (h Hash) String(val string) Hash { + // errors from WriteString are always nil for xxhash + // if it returns an err then panic + _, err := h.d.WriteString(val) + if err != nil { + panic("xxhash write of string failed: " + err.Error()) + } + return h +} + +// Sum64 returns the current hash value. +func (h Hash) Sum64() uint64 { + return h.d.Sum64() +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/set.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/set.go index 64735d382e..6572c98b12 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/set.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/set.go @@ -9,6 +9,8 @@ import ( "reflect" "slices" "sort" + + "go.opentelemetry.io/otel/attribute/internal/xxhash" ) type ( @@ -23,19 +25,19 @@ type ( // the Equals method to ensure stable equivalence checking. // // Users should also use the Distinct returned from Equivalent as a map key - // instead of a Set directly. In addition to that type providing guarantees - // on stable equivalence, it may also provide performance improvements. + // instead of a Set directly. Set has relatively poor performance when used + // as a map key compared to Distinct. Set struct { - equivalent Distinct + hash uint64 + data any } - // Distinct is a unique identifier of a Set. + // Distinct is an identifier of a Set which is very likely to be unique. // - // Distinct is designed to ensure equivalence stability: comparisons will - // return the same value across versions. For this reason, Distinct should - // always be used as a map key instead of a Set. + // Distinct should be used as a map key instead of a Set for to provide better + // performance for map operations. Distinct struct { - iface any + hash uint64 } // Sortable implements sort.Interface, used for sorting KeyValue. @@ -46,15 +48,34 @@ type ( Sortable []KeyValue ) +// Compile time check these types remain comparable. +var ( + _ = isComparable(Set{}) + _ = isComparable(Distinct{}) +) + +func isComparable[T comparable](t T) T { return t } + var ( // keyValueType is used in computeDistinctReflect. - keyValueType = reflect.TypeOf(KeyValue{}) + keyValueType = reflect.TypeFor[KeyValue]() + + // emptyHash is the hash of an empty set. + emptyHash = xxhash.New().Sum64() - // emptySet is returned for empty attribute sets. - emptySet = &Set{ - equivalent: Distinct{ - iface: [0]KeyValue{}, - }, + // userDefinedEmptySet is an empty set. It was mistakenly exposed to users + // as something they can assign to, so it must remain addressable and + // mutable. + // + // This is kept for backwards compatibility, but should not be used in new code. + userDefinedEmptySet = &Set{ + hash: emptyHash, + data: [0]KeyValue{}, + } + + emptySet = Set{ + hash: emptyHash, + data: [0]KeyValue{}, } ) @@ -62,33 +83,35 @@ var ( // // This is a convenience provided for optimized calling utility. func EmptySet() *Set { - return emptySet -} - -// reflectValue abbreviates reflect.ValueOf(d). -func (d Distinct) reflectValue() reflect.Value { - return reflect.ValueOf(d.iface) + // Continue to return the pointer to the user-defined empty set for + // backwards-compatibility. + // + // New code should not use this, instead use emptySet. + return userDefinedEmptySet } // Valid reports whether this value refers to a valid Set. -func (d Distinct) Valid() bool { - return d.iface != nil +func (d Distinct) Valid() bool { return d.hash != 0 } + +// reflectValue abbreviates reflect.ValueOf(d). +func (l Set) reflectValue() reflect.Value { + return reflect.ValueOf(l.data) } // Len returns the number of attributes in this set. func (l *Set) Len() int { - if l == nil || !l.equivalent.Valid() { + if l == nil || l.hash == 0 { return 0 } - return l.equivalent.reflectValue().Len() + return l.reflectValue().Len() } // Get returns the KeyValue at ordered position idx in this set. func (l *Set) Get(idx int) (KeyValue, bool) { - if l == nil || !l.equivalent.Valid() { + if l == nil || l.hash == 0 { return KeyValue{}, false } - value := l.equivalent.reflectValue() + value := l.reflectValue() if idx >= 0 && idx < value.Len() { // Note: The Go compiler successfully avoids an allocation for @@ -101,10 +124,10 @@ func (l *Set) Get(idx int) (KeyValue, bool) { // Value returns the value of a specified key in this set. func (l *Set) Value(k Key) (Value, bool) { - if l == nil || !l.equivalent.Valid() { + if l == nil || l.hash == 0 { return Value{}, false } - rValue := l.equivalent.reflectValue() + rValue := l.reflectValue() vlen := rValue.Len() idx := sort.Search(vlen, func(idx int) bool { @@ -144,20 +167,29 @@ func (l *Set) ToSlice() []KeyValue { return iter.ToSlice() } -// Equivalent returns a value that may be used as a map key. The Distinct type -// guarantees that the result will equal the equivalent. Distinct value of any +// Equivalent returns a value that may be used as a map key. Equal Distinct +// values are very likely to be equivalent attribute Sets. Distinct value of any // attribute set with the same elements as this, where sets are made unique by // choosing the last value in the input for any given key. func (l *Set) Equivalent() Distinct { - if l == nil || !l.equivalent.Valid() { - return emptySet.equivalent + if l == nil || l.hash == 0 { + return Distinct{hash: emptySet.hash} } - return l.equivalent + return Distinct{hash: l.hash} } // Equals reports whether the argument set is equivalent to this set. func (l *Set) Equals(o *Set) bool { - return l.Equivalent() == o.Equivalent() + if l.Equivalent() != o.Equivalent() { + return false + } + if l == nil || l.hash == 0 { + l = &emptySet + } + if o == nil || o.hash == 0 { + o = &emptySet + } + return l.data == o.data } // Encoded returns the encoded form of this set, according to encoder. @@ -169,12 +201,6 @@ func (l *Set) Encoded(encoder Encoder) string { return encoder.Encode(l.Iter()) } -func empty() Set { - return Set{ - equivalent: emptySet.equivalent, - } -} - // NewSet returns a new Set. See the documentation for // NewSetWithSortableFiltered for more details. // @@ -204,7 +230,7 @@ func NewSetWithSortable(kvs []KeyValue, _ *Sortable) Set { func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) { // Check for empty set. if len(kvs) == 0 { - return empty(), nil + return emptySet, nil } // Stable sort so the following de-duplication can implement @@ -233,10 +259,10 @@ func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) { if filter != nil { if div := filteredToFront(kvs, filter); div != 0 { - return Set{equivalent: computeDistinct(kvs[div:])}, kvs[:div] + return newSet(kvs[div:]), kvs[:div] } } - return Set{equivalent: computeDistinct(kvs)}, nil + return newSet(kvs), nil } // NewSetWithSortableFiltered returns a new Set. @@ -316,7 +342,7 @@ func (l *Set) Filter(re Filter) (Set, []KeyValue) { if first == 0 { // It is safe to assume len(slice) >= 1 given we found at least one // attribute above that needs to be filtered out. - return Set{equivalent: computeDistinct(slice[1:])}, slice[:1] + return newSet(slice[1:]), slice[:1] } // Move the filtered slice[first] to the front (preserving order). @@ -326,25 +352,24 @@ func (l *Set) Filter(re Filter) (Set, []KeyValue) { // Do not re-evaluate re(slice[first+1:]). div := filteredToFront(slice[1:first+1], re) + 1 - return Set{equivalent: computeDistinct(slice[div:])}, slice[:div] + return newSet(slice[div:]), slice[:div] } -// computeDistinct returns a Distinct using either the fixed- or -// reflect-oriented code path, depending on the size of the input. The input -// slice is assumed to already be sorted and de-duplicated. -func computeDistinct(kvs []KeyValue) Distinct { - iface := computeDistinctFixed(kvs) - if iface == nil { - iface = computeDistinctReflect(kvs) +// newSet returns a new set based on the sorted and uniqued kvs. +func newSet(kvs []KeyValue) Set { + s := Set{ + hash: hashKVs(kvs), + data: computeDataFixed(kvs), } - return Distinct{ - iface: iface, + if s.data == nil { + s.data = computeDataReflect(kvs) } + return s } -// computeDistinctFixed computes a Distinct for small slices. It returns nil -// if the input is too large for this code path. -func computeDistinctFixed(kvs []KeyValue) any { +// computeDataFixed computes a Set data for small slices. It returns nil if the +// input is too large for this code path. +func computeDataFixed(kvs []KeyValue) any { switch len(kvs) { case 1: return [1]KeyValue(kvs) @@ -371,9 +396,9 @@ func computeDistinctFixed(kvs []KeyValue) any { } } -// computeDistinctReflect computes a Distinct using reflection, works for any -// size input. -func computeDistinctReflect(kvs []KeyValue) any { +// computeDataReflect computes a Set data using reflection, works for any size +// input. +func computeDataReflect(kvs []KeyValue) any { at := reflect.New(reflect.ArrayOf(len(kvs), keyValueType)).Elem() for i, keyValue := range kvs { *(at.Index(i).Addr().Interface().(*KeyValue)) = keyValue @@ -383,7 +408,7 @@ func computeDistinctReflect(kvs []KeyValue) any { // MarshalJSON returns the JSON encoding of the Set. func (l *Set) MarshalJSON() ([]byte, error) { - return json.Marshal(l.equivalent.iface) + return json.Marshal(l.data) } // MarshalLog is the marshaling function used by the logging system to represent this Set. diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/type_string.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/type_string.go index e584b24776..24f1fa37db 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/type_string.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/type_string.go @@ -24,8 +24,9 @@ const _Type_name = "INVALIDBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICE var _Type_index = [...]uint8{0, 7, 11, 16, 23, 29, 38, 48, 60, 71} func (i Type) String() string { - if i < 0 || i >= Type(len(_Type_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_Type_index)-1 { return "Type(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Type_name[_Type_index[i]:_Type_index[i+1]] + return _Type_name[_Type_index[idx]:_Type_index[idx+1]] } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/value.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/value.go index 653c33a861..5931e71291 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/value.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/attribute/value.go @@ -66,8 +66,7 @@ func IntValue(v int) Value { // IntSliceValue creates an INTSLICE Value. func IntSliceValue(v []int) Value { - var int64Val int64 - cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(int64Val))) + cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[int64]())) for i, val := range v { cp.Elem().Index(i).SetInt(int64(val)) } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/baggage/baggage.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/baggage/baggage.go index f83a448ec6..c4093e49ae 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/baggage/baggage.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/baggage/baggage.go @@ -317,7 +317,7 @@ func parseMember(member string) (Member, error) { keyValue, properties, found := strings.Cut(member, propertyDelimiter) if found { // Parse the member properties. - for _, pStr := range strings.Split(properties, propertyDelimiter) { + for pStr := range strings.SplitSeq(properties, propertyDelimiter) { p, err := parseProperty(pStr) if err != nil { return newInvalidMember(), err @@ -480,7 +480,7 @@ func Parse(bStr string) (Baggage, error) { } b := make(baggage.List) - for _, memberStr := range strings.Split(bStr, listDelimiter) { + for memberStr := range strings.SplitSeq(bStr, listDelimiter) { m, err := parseMember(memberStr) if err != nil { return Baggage{}, err @@ -648,7 +648,7 @@ func parsePropertyInternal(s string) (p Property, ok bool) { // If we couldn't find any valid key character, // it means the key is either empty or invalid. if keyStart == keyEnd { - return + return p, ok } // Skip spaces after the key: " key< >= value ". @@ -658,13 +658,13 @@ func parsePropertyInternal(s string) (p Property, ok bool) { // A key can have no value, like: " key ". ok = true p.key = s[keyStart:keyEnd] - return + return p, ok } // If we have not reached the end and we can't find the '=' delimiter, // it means the property is invalid. if s[index] != keyValueDelimiter[0] { - return + return p, ok } // Attempting to parse the value. @@ -690,14 +690,14 @@ func parsePropertyInternal(s string) (p Property, ok bool) { // we have not reached the end, it means the property is // invalid, something like: " key = value value1". if index != len(s) { - return + return p, ok } // Decode a percent-encoded value. rawVal := s[valueStart:valueEnd] unescapeVal, err := url.PathUnescape(rawVal) if err != nil { - return + return p, ok } value := replaceInvalidUTF8Sequences(len(rawVal), unescapeVal) @@ -706,7 +706,7 @@ func parsePropertyInternal(s string) (p Property, ok bool) { p.hasValue = true p.value = value - return + return p, ok } func skipSpace(s string, offset int) int { diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile index a311fbb483..676e79116d 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile @@ -1,4 +1,4 @@ # This is a renovate-friendly source of Docker images. FROM python:3.13.6-slim-bullseye@sha256:e98b521460ee75bca92175c16247bdf7275637a8faaeb2bcfa19d879ae5c4b9a AS python -FROM otel/weaver:v0.17.1@sha256:32523b5e44fb44418786347e9f7dde187d8797adb6d57a2ee99c245346c3cdfe AS weaver +FROM otel/weaver:v0.20.0@sha256:fa4f1c6954ecea78ab1a4e865bd6f5b4aaba80c1896f9f4a11e2c361d04e197e AS weaver FROM avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee AS markdown diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go index 379bc8170d..d431fc4517 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go @@ -113,7 +113,7 @@ func span(sd tracesdk.ReadOnlySpan) *tracepb.Span { if psid := sd.Parent().SpanID(); psid.IsValid() { s.ParentSpanId = psid[:] } - s.Flags = buildSpanFlags(sd.Parent()) + s.Flags = buildSpanFlagsWith(sd.SpanContext().TraceFlags(), sd.Parent()) return s } @@ -159,7 +159,7 @@ func links(links []tracesdk.Link) []*tracepb.Span_Link { tid := otLink.SpanContext.TraceID() sid := otLink.SpanContext.SpanID() - flags := buildSpanFlags(otLink.SpanContext) + flags := buildSpanFlagsWith(otLink.SpanContext.TraceFlags(), otLink.SpanContext) sl = append(sl, &tracepb.Span_Link{ TraceId: tid[:], @@ -172,13 +172,15 @@ func links(links []tracesdk.Link) []*tracepb.Span_Link { return sl } -func buildSpanFlags(sc trace.SpanContext) uint32 { - flags := tracepb.SpanFlags_SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK - if sc.IsRemote() { - flags |= tracepb.SpanFlags_SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK +func buildSpanFlagsWith(tf trace.TraceFlags, parent trace.SpanContext) uint32 { + // Lower 8 bits are the W3C TraceFlags; always indicate that we know whether the parent is remote + flags := uint32(tf) | uint32(tracepb.SpanFlags_SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) + // Set the parent-is-remote bit when applicable + if parent.IsRemote() { + flags |= uint32(tracepb.SpanFlags_SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) } - return uint32(flags) // nolint:gosec // Flags is a bitmask and can't be negative + return flags // nolint:gosec // Flags is a bitmask and can't be negative } // spanEvents transforms span Events to an OTLP span events. diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/client.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/client.go index 4b4cc76f4a..76b7cd461b 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/client.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/client.go @@ -17,9 +17,10 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" - "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry" ) @@ -45,6 +46,9 @@ type client struct { conn *grpc.ClientConn tscMu sync.RWMutex tsc coltracepb.TraceServiceClient + + instID int64 + inst *observ.Instrumentation } // Compile time check *client implements otlptrace.Client. @@ -68,6 +72,7 @@ func newClient(opts ...Option) *client { stopCtx: ctx, stopFunc: cancel, conn: cfg.GRPCConn, + instID: counter.NextExporterID(), } if len(cfg.Traces.Headers) > 0 { @@ -92,13 +97,24 @@ func (c *client) Start(context.Context) error { c.conn = conn } + // Initialize the instrumentation if not already done. + // + // Initialize here instead of NewClient to allow any errors to be passed + // back to the caller and so that any setup of the environment variables to + // enable instrumentation can be set via code. + var err error + if c.inst == nil { + target := c.conn.CanonicalTarget() + c.inst, err = observ.NewInstrumentation(c.instID, target) + } + // The otlptrace.Client interface states this method is called just once, // so no need to check if already started. c.tscMu.Lock() c.tsc = coltracepb.NewTraceServiceClient(c.conn) c.tscMu.Unlock() - return nil + return err } var errAlreadyStopped = errors.New("the client is already stopped") @@ -174,7 +190,7 @@ var errShutdown = errors.New("the client is shutdown") // // Retryable errors from the server will be handled according to any // RetryConfig the client was created with. -func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) error { +func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) (uploadErr error) { // Hold a read lock to ensure a shut down initiated after this starts does // not abandon the export. This read lock acquire has less priority than a // write lock acquire (i.e. Stop), meaning if the client is shutting down @@ -189,6 +205,12 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc ctx, cancel := c.exportContext(ctx) defer cancel() + var code codes.Code + if c.inst != nil { + op := c.inst.ExportSpans(ctx, len(protoSpans)) + defer func() { op.End(uploadErr, code) }() + } + return c.requestFunc(ctx, func(iCtx context.Context) error { resp, err := c.tsc.Export(iCtx, &coltracepb.ExportTraceServiceRequest{ ResourceSpans: protoSpans, @@ -197,16 +219,17 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc msg := resp.PartialSuccess.GetErrorMessage() n := resp.PartialSuccess.GetRejectedSpans() if n != 0 || msg != "" { - err := internal.TracePartialSuccessError(n, msg) - otel.Handle(err) + e := internal.TracePartialSuccessError(n, msg) + uploadErr = errors.Join(uploadErr, e) } } // nil is converted to OK. - if status.Code(err) == codes.OK { + code = status.Code(err) + if code == codes.OK { // Success. - return nil + return uploadErr } - return err + return errors.Join(uploadErr, err) }) } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter/counter.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter/counter.go new file mode 100644 index 0000000000..323b2a2c9a --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter/counter.go @@ -0,0 +1,31 @@ +// Code generated by gotmpl. DO NOT MODIFY. +// source: internal/shared/counter/counter.go.tmpl + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package counter provides a simple counter for generating unique IDs. +// +// This package is used to generate unique IDs while allowing testing packages +// to reset the counter. +package counter // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter" + +import "sync/atomic" + +// exporterN is a global 0-based count of the number of exporters created. +var exporterN atomic.Int64 + +// NextExporterID returns the next unique ID for an exporter. +func NextExporterID() int64 { + const inc = 1 + return exporterN.Add(inc) - inc +} + +// SetExporterID sets the exporter ID counter to v and returns the previous +// value. +// +// This function is useful for testing purposes, allowing you to reset the +// counter. It should not be used in production code. +func SetExporterID(v int64) int64 { + return exporterN.Swap(v) +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/gen.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/gen.go index b6e6b10fbf..7fe9c9f3a3 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/gen.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/gen.go @@ -23,3 +23,12 @@ package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/ot //go:generate gotmpl --body=../../../../../internal/shared/otlp/otlptrace/otlptracetest/collector.go.tmpl "--data={}" --out=otlptracetest/collector.go //go:generate gotmpl --body=../../../../../internal/shared/otlp/otlptrace/otlptracetest/data.go.tmpl "--data={}" --out=otlptracetest/data.go //go:generate gotmpl --body=../../../../../internal/shared/otlp/otlptrace/otlptracetest/otlptest.go.tmpl "--data={}" --out=otlptracetest/otlptest.go + +//go:generate gotmpl --body=../../../../../internal/shared/x/x.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\" }" --out=x/x.go +//go:generate gotmpl --body=../../../../../internal/shared/x/x_test.go.tmpl "--data={}" --out=x/x_test.go + +//go:generate gotmpl --body=../../../../../internal/shared/otlp/observ/target.go.tmpl "--data={ \"pkg\": \"observ\", \"pkg_path\": \"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ\" }" --out=observ/target.go +//go:generate gotmpl --body=../../../../../internal/shared/otlp/observ/target_test.go.tmpl "--data={ \"pkg\": \"observ\" }" --out=observ/target_test.go + +//go:generate gotmpl --body=../../../../../internal/shared/counter/counter.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter\" }" --out=counter/counter.go +//go:generate gotmpl --body=../../../../../internal/shared/counter/counter_test.go.tmpl "--data={}" --out=counter/counter_test.go diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/doc.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/doc.go new file mode 100644 index 0000000000..0dd54e4b96 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/doc.go @@ -0,0 +1,6 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package observ provides experimental observability instrumentation for the +// otlptracegrpc exporter. +package observ // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/instrumentation.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/instrumentation.go new file mode 100644 index 0000000000..d4a69f4d7c --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/instrumentation.go @@ -0,0 +1,342 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package observ // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ" + +import ( + "context" + "errors" + "fmt" + "sync" + "time" + + "google.golang.org/grpc/codes" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x" + "go.opentelemetry.io/otel/internal/global" + "go.opentelemetry.io/otel/metric" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" + "go.opentelemetry.io/otel/semconv/v1.39.0/otelconv" +) + +const ( + // ScopeName is the unique name of the meter used for instrumentation. + ScopeName = "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ" + + // SchemaURL is the schema URL of the metrics produced by this + // instrumentation. + SchemaURL = semconv.SchemaURL + + // Version is the current version of this instrumentation. + // + // This matches the version of the exporter. + Version = internal.Version +) + +var ( + measureAttrsPool = &sync.Pool{ + New: func() any { + const n = 1 + // component.name + 1 + // component.type + 1 + // server.addr + 1 + // server.port + 1 + // error.type + 1 // rpc.grpc.status_code + s := make([]attribute.KeyValue, 0, n) + // Return a pointer to a slice instead of a slice itself + // to avoid allocations on every call. + return &s + }, + } + + addOptPool = &sync.Pool{ + New: func() any { + const n = 1 // WithAttributeSet + o := make([]metric.AddOption, 0, n) + return &o + }, + } + + recordOptPool = &sync.Pool{ + New: func() any { + const n = 1 // WithAttributeSet + o := make([]metric.RecordOption, 0, n) + return &o + }, + } +) + +func get[T any](p *sync.Pool) *[]T { return p.Get().(*[]T) } + +func put[T any](p *sync.Pool, s *[]T) { + *s = (*s)[:0] // Reset. + p.Put(s) +} + +// ComponentName returns the component name for the exporter with the +// provided ID. +func ComponentName(id int64) string { + t := semconv.OTelComponentTypeOtlpGRPCSpanExporter.Value.AsString() + return fmt.Sprintf("%s/%d", t, id) +} + +// Instrumentation is experimental instrumentation for the exporter. +type Instrumentation struct { + inflightSpans metric.Int64UpDownCounter + exportedSpans metric.Int64Counter + opDuration metric.Float64Histogram + + attrs []attribute.KeyValue + addOpt metric.AddOption + recOpt metric.RecordOption +} + +// NewInstrumentation returns instrumentation for an OTLP over gPRC trace +// exporter with the provided ID using the global MeterProvider. +// +// The id should be the unique exporter instance ID. It is used +// to set the "component.name" attribute. +// +// The target is the endpoint the exporter is exporting to. +// +// If the experimental observability is disabled, nil is returned. +func NewInstrumentation(id int64, target string) (*Instrumentation, error) { + if !x.Observability.Enabled() { + return nil, nil + } + + attrs := BaseAttrs(id, target) + i := &Instrumentation{ + attrs: attrs, + addOpt: metric.WithAttributeSet(attribute.NewSet(attrs...)), + + // Do not modify attrs (NewSet sorts in-place), make a new slice. + recOpt: metric.WithAttributeSet(attribute.NewSet(append( + // Default to OK status code. + []attribute.KeyValue{ + semconv.RPCResponseStatusCode(codes.OK.String()), + }, + attrs..., + )...)), + } + + mp := otel.GetMeterProvider() + m := mp.Meter( + ScopeName, + metric.WithInstrumentationVersion(Version), + metric.WithSchemaURL(SchemaURL), + ) + + var err error + + inflightSpans, e := otelconv.NewSDKExporterSpanInflight(m) + if e != nil { + e = fmt.Errorf("failed to create span inflight metric: %w", e) + err = errors.Join(err, e) + } + i.inflightSpans = inflightSpans.Inst() + + exportedSpans, e := otelconv.NewSDKExporterSpanExported(m) + if e != nil { + e = fmt.Errorf("failed to create span exported metric: %w", e) + err = errors.Join(err, e) + } + i.exportedSpans = exportedSpans.Inst() + + opDuration, e := otelconv.NewSDKExporterOperationDuration(m) + if e != nil { + e = fmt.Errorf("failed to create operation duration metric: %w", e) + err = errors.Join(err, e) + } + i.opDuration = opDuration.Inst() + + return i, err +} + +// BaseAttrs returns the base attributes for the exporter with the provided ID +// and target. +// +// The id should be the unique exporter instance ID. It is used +// to set the "component.name" attribute. +// +// The target is the gRPC target the exporter is exporting to. It is expected +// to be the output of the Client's CanonicalTarget method. +func BaseAttrs(id int64, target string) []attribute.KeyValue { + host, port, err := ParseCanonicalTarget(target) + if err != nil || (host == "" && port < 0) { + if err != nil { + global.Debug("failed to parse target", "target", target, "error", err) + } + return []attribute.KeyValue{ + semconv.OTelComponentName(ComponentName(id)), + semconv.OTelComponentTypeOtlpGRPCSpanExporter, + } + } + + // Do not use append so the slice is exactly allocated. + + if port < 0 { + return []attribute.KeyValue{ + semconv.OTelComponentName(ComponentName(id)), + semconv.OTelComponentTypeOtlpGRPCSpanExporter, + semconv.ServerAddress(host), + } + } + + if host == "" { + return []attribute.KeyValue{ + semconv.OTelComponentName(ComponentName(id)), + semconv.OTelComponentTypeOtlpGRPCSpanExporter, + semconv.ServerPort(port), + } + } + + return []attribute.KeyValue{ + semconv.OTelComponentName(ComponentName(id)), + semconv.OTelComponentTypeOtlpGRPCSpanExporter, + semconv.ServerAddress(host), + semconv.ServerPort(port), + } +} + +// ExportSpans instruments the ExportSpans method of the exporter. It returns +// an [ExportOp] that must have its [ExportOp.End] method called when the +// ExportSpans method returns. +func (i *Instrumentation) ExportSpans(ctx context.Context, nSpans int) ExportOp { + start := time.Now() + + addOpt := get[metric.AddOption](addOptPool) + defer put(addOptPool, addOpt) + *addOpt = append(*addOpt, i.addOpt) + i.inflightSpans.Add(ctx, int64(nSpans), *addOpt...) + + return ExportOp{ + ctx: ctx, + start: start, + nSpans: int64(nSpans), + inst: i, + } +} + +// ExportOp tracks the operation being observed by [Instrumentation.ExportSpans]. +type ExportOp struct { + ctx context.Context + start time.Time + nSpans int64 + + inst *Instrumentation +} + +// End completes the observation of the operation being observed by a call to +// [Instrumentation.ExportSpans]. +// +// Any error that is encountered is provided as err. +// +// If err is not nil, all spans will be recorded as failures unless error is of +// type [internal.PartialSuccess]. In the case of a PartialSuccess, the number +// of successfully exported spans will be determined by inspecting the +// RejectedItems field of the PartialSuccess. +func (e ExportOp) End(err error, code codes.Code) { + addOpt := get[metric.AddOption](addOptPool) + defer put(addOptPool, addOpt) + *addOpt = append(*addOpt, e.inst.addOpt) + + e.inst.inflightSpans.Add(e.ctx, -e.nSpans, *addOpt...) + + success := successful(e.nSpans, err) + // Record successfully exported spans, even if the value is 0 which are + // meaningful to distribution aggregations. + e.inst.exportedSpans.Add(e.ctx, success, *addOpt...) + + if err != nil { + attrs := get[attribute.KeyValue](measureAttrsPool) + defer put(measureAttrsPool, attrs) + *attrs = append(*attrs, e.inst.attrs...) + *attrs = append(*attrs, semconv.ErrorType(err)) + + // Do not inefficiently make a copy of attrs by using + // WithAttributes instead of WithAttributeSet. + o := metric.WithAttributeSet(attribute.NewSet(*attrs...)) + // Reset addOpt with new attribute set. + *addOpt = append((*addOpt)[:0], o) + + e.inst.exportedSpans.Add(e.ctx, e.nSpans-success, *addOpt...) + } + + recOpt := get[metric.RecordOption](recordOptPool) + defer put(recordOptPool, recOpt) + *recOpt = append(*recOpt, e.inst.recordOption(err, code)) + + d := time.Since(e.start).Seconds() + e.inst.opDuration.Record(e.ctx, d, *recOpt...) +} + +// recordOption returns a RecordOption with attributes representing the +// outcome of the operation being recorded. +// +// If err is nil and code is codes.OK, the default recOpt of the +// Instrumentation is returned. +// +// If err is not nil or code is not codes.OK, a new RecordOption is returned +// with the base attributes of the Instrumentation plus the rpc.grpc.status_code +// attribute set to the provided code, and if err is not nil, the error.type +// attribute set to the type of the error. +func (i *Instrumentation) recordOption(err error, code codes.Code) metric.RecordOption { + if err == nil && code == codes.OK { + return i.recOpt + } + + attrs := get[attribute.KeyValue](measureAttrsPool) + defer put(measureAttrsPool, attrs) + *attrs = append(*attrs, i.attrs...) + + *attrs = append(*attrs, semconv.RPCResponseStatusCode(code.String())) + if err != nil { + *attrs = append(*attrs, semconv.ErrorType(err)) + } + + // Do not inefficiently make a copy of attrs by using WithAttributes + // instead of WithAttributeSet. + return metric.WithAttributeSet(attribute.NewSet(*attrs...)) +} + +// successful returns the number of successfully exported spans out of the n +// that were exported based on the provided error. +// +// If err is nil, n is returned. All spans were successfully exported. +// +// If err is not nil and not an [internal.PartialSuccess] error, 0 is returned. +// It is assumed all spans failed to be exported. +// +// If err is an [internal.PartialSuccess] error, the number of successfully +// exported spans is computed by subtracting the RejectedItems field from n. If +// RejectedItems is negative, n is returned. If RejectedItems is greater than +// n, 0 is returned. +func successful(n int64, err error) int64 { + if err == nil { + return n // All spans successfully exported. + } + // Split rejection calculation so successful is inlinable. + return n - rejected(n, err) +} + +var errPartialPool = &sync.Pool{ + New: func() any { return new(internal.PartialSuccess) }, +} + +// rejected returns how many out of the n spans exporter were rejected based on +// the provided non-nil err. +func rejected(n int64, err error) int64 { + ps := errPartialPool.Get().(*internal.PartialSuccess) + defer errPartialPool.Put(ps) + // Check for partial success. + if errors.As(err, ps) { + // Bound RejectedItems to [0, n]. This should not be needed, + // but be defensive as this is from an external source. + return min(max(ps.RejectedItems, 0), n) + } + return n // All spans rejected. +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/target.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/target.go new file mode 100644 index 0000000000..34eee27db0 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/target.go @@ -0,0 +1,143 @@ +// Code generated by gotmpl. DO NOT MODIFY. +// source: internal/shared/otlp/observ/target.go.tmpl + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package observ // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ" + +import ( + "errors" + "fmt" + "net" + "net/netip" + "strconv" + "strings" +) + +const ( + schemeUnix = "unix" + schemeUnixAbstract = "unix-abstract" +) + +// ParseCanonicalTarget parses a target string and returns the extracted host +// (domain address or IP), the target port, or an error. +// +// If no port is specified, -1 is returned. +// +// If no host is specified, an empty string is returned. +// +// The target string is expected to always have the form +// "://[authority]/". For example: +// - "dns:///example.com:42" +// - "dns://8.8.8.8/example.com:42" +// - "unix:///path/to/socket" +// - "unix-abstract:///socket-name" +// - "passthrough:///192.34.2.1:42" +// +// The target is expected to come from the CanonicalTarget method of a gRPC +// Client. +func ParseCanonicalTarget(target string) (string, int, error) { + const sep = "://" + + // Find scheme. Do not allocate the string by using url.Parse. + idx := strings.Index(target, sep) + if idx == -1 { + return "", -1, fmt.Errorf("invalid target %q: missing scheme", target) + } + scheme, endpoint := target[:idx], target[idx+len(sep):] + + // Check for unix schemes. + if scheme == schemeUnix || scheme == schemeUnixAbstract { + return parseUnix(endpoint) + } + + // Strip leading slash and any authority. + if i := strings.Index(endpoint, "/"); i != -1 { + endpoint = endpoint[i+1:] + } + + // DNS, passthrough, and custom resolvers. + return parseEndpoint(endpoint) +} + +// parseUnix parses unix socket targets. +func parseUnix(endpoint string) (string, int, error) { + // Format: unix[-abstract]://path + // + // We should have "/path" (empty authority) if valid. + if len(endpoint) >= 1 && endpoint[0] == '/' { + // Return the full path including leading slash. + return endpoint, -1, nil + } + + // If there's no leading slash, it means there might be an authority + // Check for authority case (should error): "authority/path" + if slashIdx := strings.Index(endpoint, "/"); slashIdx > 0 { + return "", -1, fmt.Errorf("invalid (non-empty) authority: %s", endpoint[:slashIdx]) + } + + return "", -1, errors.New("invalid unix target format") +} + +// parseEndpoint parses an endpoint from a gRPC target. +// +// It supports the following formats: +// - "host" +// - "host%zone" +// - "host:port" +// - "host%zone:port" +// - "ipv4" +// - "ipv4%zone" +// - "ipv4:port" +// - "ipv4%zone:port" +// - "ipv6" +// - "ipv6%zone" +// - "[ipv6]" +// - "[ipv6%zone]" +// - "[ipv6]:port" +// - "[ipv6%zone]:port" +// +// It returns the host or host%zone (domain address or IP), the port (or -1 if +// not specified), or an error if the input is not a valid. +func parseEndpoint(endpoint string) (string, int, error) { + // First check if the endpoint is just an IP address. + if ip := parseIP(endpoint); ip != "" { + return ip, -1, nil + } + + // If there's no colon, there is no port (IPv6 with no port checked above). + if !strings.Contains(endpoint, ":") { + return endpoint, -1, nil + } + + host, portStr, err := net.SplitHostPort(endpoint) + if err != nil { + return "", -1, fmt.Errorf("invalid host:port %q: %w", endpoint, err) + } + + const base, bitSize = 10, 16 + port16, err := strconv.ParseUint(portStr, base, bitSize) + if err != nil { + return "", -1, fmt.Errorf("invalid port %q: %w", portStr, err) + } + port := int(port16) // port is guaranteed to be in the range [0, 65535]. + + return host, port, nil +} + +// parseIP attempts to parse the entire endpoint as an IP address. +// It returns the normalized string form of the IP if successful, +// or an empty string if parsing fails. +func parseIP(ip string) string { + // Strip leading and trailing brackets for IPv6 addresses. + if len(ip) >= 2 && ip[0] == '[' && ip[len(ip)-1] == ']' { + ip = ip[1 : len(ip)-1] + } + addr, err := netip.ParseAddr(ip) + if err != nil { + return "" + } + // Return the normalized string form of the IP. + return addr.String() +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/partialsuccess.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/partialsuccess.go index 1c46594233..a811a07b35 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/partialsuccess.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/partialsuccess.go @@ -29,6 +29,17 @@ func (ps PartialSuccess) Error() string { return fmt.Sprintf("OTLP partial success: %s (%d %s rejected)", msg, ps.RejectedItems, ps.RejectedKind) } +// As returns true if ps can be assigned to target and makes the assignment. +// Otherwise, it returns false. This supports the errors.As() interface. +func (ps PartialSuccess) As(target any) bool { + t, ok := target.(*PartialSuccess) + if !ok { + return false + } + *t = ps + return true +} + // Is supports the errors.Is() interface. func (ps PartialSuccess) Is(err error) bool { _, ok := err.(PartialSuccess) diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry/retry.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry/retry.go index 259a898ae7..a7b8e81a78 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry/retry.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry/retry.go @@ -94,6 +94,11 @@ func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc { return err } + // Check if context is canceled before attempting to wait and retry. + if ctx.Err() != nil { + return fmt.Errorf("%w: %w", ctx.Err(), err) + } + if maxElapsedTime != 0 && time.Since(startTime) > maxElapsedTime { return fmt.Errorf("max retry time elapsed: %w", err) } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/version.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/version.go new file mode 100644 index 0000000000..19fac1b725 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/version.go @@ -0,0 +1,8 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal" + +// Version is the current release version of the OpenTelemetry OTLP gRPC trace +// exporter in use. +const Version = "1.40.0" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/x/README.md b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/README.md similarity index 54% rename from cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/x/README.md rename to cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/README.md index feec16fa64..15a3011bff 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/x/README.md +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/README.md @@ -1,33 +1,34 @@ # Experimental Features -The Trace SDK contains features that have not yet stabilized in the OpenTelemetry specification. -These features are added to the OpenTelemetry Go Trace SDK prior to stabilization in the specification so that users can start experimenting with them and provide feedback. +The `otlptracegrpc` exporter contains features that have not yet stabilized in the OpenTelemetry specification. +These features are added to the `otlptracegrpc` exporter prior to stabilization in the specification so that users can start experimenting with them and provide feedback. -These features may change in backwards incompatible ways as feedback is applied. +These feature may change in backwards incompatible ways as feedback is applied. See the [Compatibility and Stability](#compatibility-and-stability) section for more information. ## Features -- [Self-Observability](#self-observability) +- [Observability](#observability) -### Self-Observability +### Observability -The SDK provides a self-observability feature that allows you to monitor the SDK itself. +The `otlptracegrpc` exporter provides a observability feature that allows you to monitor the SDK itself. -To opt-in, set the environment variable `OTEL_GO_X_SELF_OBSERVABILITY` to `true`. +To opt-in, set the environment variable `OTEL_GO_X_OBSERVABILITY` to `true`. When enabled, the SDK will create the following metrics using the global `MeterProvider`: -- `otel.sdk.span.live` -- `otel.sdk.span.started` +- `otel.sdk.exporter.span.inflight` +- `otel.sdk.exporter.span.exported` +- `otel.sdk.exporter.operation.duration` Please see the [Semantic conventions for OpenTelemetry SDK metrics] documentation for more details on these metrics. -[Semantic conventions for OpenTelemetry SDK metrics]: https://github.com/open-telemetry/semantic-conventions/blob/v1.36.0/docs/otel/sdk-metrics.md +[Semantic conventions for OpenTelemetry SDK metrics]: https://github.com/open-telemetry/semantic-conventions/blob/v1.37.0/docs/otel/sdk-metrics.md ## Compatibility and Stability -Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../../../VERSIONING.md). +Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../../../../../VERSIONING.md). These features may be removed or modified in successive version releases, including patch versions. When an experimental feature is promoted to a stable feature, a migration path will be included in the changelog entry of the release. diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/observ.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/observ.go new file mode 100644 index 0000000000..4e89c6524f --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/observ.go @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package x // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x" + +import "strings" + +// Observability is an experimental feature flag that determines if exporter +// observability metrics are enabled. +// +// To enable this feature set the OTEL_GO_X_OBSERVABILITY environment variable +// to the case-insensitive string value of "true" (i.e. "True" and "TRUE" +// will also enable this). +var Observability = newFeature( + []string{"OBSERVABILITY"}, + func(v string) (string, bool) { + if strings.EqualFold(v, "true") { + return v, true + } + return "", false + }, +) diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/x/x.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/x.go similarity index 55% rename from cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/x/x.go rename to cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/x.go index 2fcbbcc66e..741ba62c97 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/x/x.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/x.go @@ -1,45 +1,38 @@ +// Code generated by gotmpl. DO NOT MODIFY. +// source: internal/shared/x/x.go.tmpl + // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -// Package x documents experimental features for [go.opentelemetry.io/otel/sdk/trace]. -package x // import "go.opentelemetry.io/otel/sdk/trace/internal/x" +// Package x documents experimental features for [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc]. +package x // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x" import ( "os" - "strings" ) -// SelfObservability is an experimental feature flag that determines if SDK -// self-observability metrics are enabled. -// -// To enable this feature set the OTEL_GO_X_SELF_OBSERVABILITY environment variable -// to the case-insensitive string value of "true" (i.e. "True" and "TRUE" -// will also enable this). -var SelfObservability = newFeature("SELF_OBSERVABILITY", func(v string) (string, bool) { - if strings.EqualFold(v, "true") { - return v, true - } - return "", false -}) - // Feature is an experimental feature control flag. It provides a uniform way // to interact with these feature flags and parse their values. type Feature[T any] struct { - key string + keys []string parse func(v string) (T, bool) } -func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] { +func newFeature[T any](suffix []string, parse func(string) (T, bool)) Feature[T] { const envKeyRoot = "OTEL_GO_X_" + keys := make([]string, 0, len(suffix)) + for _, s := range suffix { + keys = append(keys, envKeyRoot+s) + } return Feature[T]{ - key: envKeyRoot + suffix, + keys: keys, parse: parse, } } -// Key returns the environment variable key that needs to be set to enable the +// Keys returns the environment variable keys that can be set to enable the // feature. -func (f Feature[T]) Key() string { return f.key } +func (f Feature[T]) Keys() []string { return f.keys } // Lookup returns the user configured value for the feature and true if the // user has enabled the feature. Otherwise, if the feature is not enabled, a @@ -49,11 +42,13 @@ func (f Feature[T]) Lookup() (v T, ok bool) { // // > The SDK MUST interpret an empty value of an environment variable the // > same way as when the variable is unset. - vRaw := os.Getenv(f.key) - if vRaw == "" { - return v, ok + for _, key := range f.keys { + vRaw := os.Getenv(key) + if vRaw != "" { + return f.parse(vRaw) + } } - return f.parse(vRaw) + return v, ok } // Enabled reports whether the feature is enabled. diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go index 3b79c1a0b5..370cd2a6f8 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go @@ -5,5 +5,5 @@ package otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace" // Version is the current release version of the OpenTelemetry OTLP trace exporter in use. func Version() string { - return "1.38.0" + return "1.40.0" } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/internal/global/instruments.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/internal/global/instruments.go index ae92a42516..55255cddfc 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/internal/global/instruments.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/internal/global/instruments.go @@ -229,6 +229,13 @@ func (i *sfCounter) Add(ctx context.Context, incr float64, opts ...metric.AddOpt } } +func (i *sfCounter) Enabled(ctx context.Context) bool { + if ctr := i.delegate.Load(); ctr != nil { + return ctr.(metric.Float64Counter).Enabled(ctx) + } + return false +} + type sfUpDownCounter struct { embedded.Float64UpDownCounter @@ -255,6 +262,13 @@ func (i *sfUpDownCounter) Add(ctx context.Context, incr float64, opts ...metric. } } +func (i *sfUpDownCounter) Enabled(ctx context.Context) bool { + if ctr := i.delegate.Load(); ctr != nil { + return ctr.(metric.Float64UpDownCounter).Enabled(ctx) + } + return false +} + type sfHistogram struct { embedded.Float64Histogram @@ -281,6 +295,13 @@ func (i *sfHistogram) Record(ctx context.Context, x float64, opts ...metric.Reco } } +func (i *sfHistogram) Enabled(ctx context.Context) bool { + if ctr := i.delegate.Load(); ctr != nil { + return ctr.(metric.Float64Histogram).Enabled(ctx) + } + return false +} + type sfGauge struct { embedded.Float64Gauge @@ -307,6 +328,13 @@ func (i *sfGauge) Record(ctx context.Context, x float64, opts ...metric.RecordOp } } +func (i *sfGauge) Enabled(ctx context.Context) bool { + if ctr := i.delegate.Load(); ctr != nil { + return ctr.(metric.Float64Gauge).Enabled(ctx) + } + return false +} + type siCounter struct { embedded.Int64Counter @@ -333,6 +361,13 @@ func (i *siCounter) Add(ctx context.Context, x int64, opts ...metric.AddOption) } } +func (i *siCounter) Enabled(ctx context.Context) bool { + if ctr := i.delegate.Load(); ctr != nil { + return ctr.(metric.Int64Counter).Enabled(ctx) + } + return false +} + type siUpDownCounter struct { embedded.Int64UpDownCounter @@ -359,6 +394,13 @@ func (i *siUpDownCounter) Add(ctx context.Context, x int64, opts ...metric.AddOp } } +func (i *siUpDownCounter) Enabled(ctx context.Context) bool { + if ctr := i.delegate.Load(); ctr != nil { + return ctr.(metric.Int64UpDownCounter).Enabled(ctx) + } + return false +} + type siHistogram struct { embedded.Int64Histogram @@ -385,6 +427,13 @@ func (i *siHistogram) Record(ctx context.Context, x int64, opts ...metric.Record } } +func (i *siHistogram) Enabled(ctx context.Context) bool { + if ctr := i.delegate.Load(); ctr != nil { + return ctr.(metric.Int64Histogram).Enabled(ctx) + } + return false +} + type siGauge struct { embedded.Int64Gauge @@ -410,3 +459,10 @@ func (i *siGauge) Record(ctx context.Context, x int64, opts ...metric.RecordOpti ctr.(metric.Int64Gauge).Record(ctx, x, opts...) } } + +func (i *siGauge) Enabled(ctx context.Context) bool { + if ctr := i.delegate.Load(); ctr != nil { + return ctr.(metric.Int64Gauge).Enabled(ctx) + } + return false +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/internal/global/meter.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/internal/global/meter.go index adb37b5b0e..50043d669b 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/internal/global/meter.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/internal/global/meter.go @@ -105,7 +105,7 @@ type delegatedInstrument interface { setDelegate(metric.Meter) } -// instID are the identifying properties of a instrument. +// instID are the identifying properties of an instrument. type instID struct { // name is the name of the stream. name string @@ -157,7 +157,7 @@ func (m *meter) Int64Counter(name string, options ...metric.Int64CounterOption) cfg := metric.NewInt64CounterConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*siCounter)(nil)), + kind: reflect.TypeFor[*siCounter](), description: cfg.Description(), unit: cfg.Unit(), } @@ -183,7 +183,7 @@ func (m *meter) Int64UpDownCounter( cfg := metric.NewInt64UpDownCounterConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*siUpDownCounter)(nil)), + kind: reflect.TypeFor[*siUpDownCounter](), description: cfg.Description(), unit: cfg.Unit(), } @@ -206,7 +206,7 @@ func (m *meter) Int64Histogram(name string, options ...metric.Int64HistogramOpti cfg := metric.NewInt64HistogramConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*siHistogram)(nil)), + kind: reflect.TypeFor[*siHistogram](), description: cfg.Description(), unit: cfg.Unit(), } @@ -229,7 +229,7 @@ func (m *meter) Int64Gauge(name string, options ...metric.Int64GaugeOption) (met cfg := metric.NewInt64GaugeConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*siGauge)(nil)), + kind: reflect.TypeFor[*siGauge](), description: cfg.Description(), unit: cfg.Unit(), } @@ -255,7 +255,7 @@ func (m *meter) Int64ObservableCounter( cfg := metric.NewInt64ObservableCounterConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*aiCounter)(nil)), + kind: reflect.TypeFor[*aiCounter](), description: cfg.Description(), unit: cfg.Unit(), } @@ -281,7 +281,7 @@ func (m *meter) Int64ObservableUpDownCounter( cfg := metric.NewInt64ObservableUpDownCounterConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*aiUpDownCounter)(nil)), + kind: reflect.TypeFor[*aiUpDownCounter](), description: cfg.Description(), unit: cfg.Unit(), } @@ -307,7 +307,7 @@ func (m *meter) Int64ObservableGauge( cfg := metric.NewInt64ObservableGaugeConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*aiGauge)(nil)), + kind: reflect.TypeFor[*aiGauge](), description: cfg.Description(), unit: cfg.Unit(), } @@ -330,7 +330,7 @@ func (m *meter) Float64Counter(name string, options ...metric.Float64CounterOpti cfg := metric.NewFloat64CounterConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*sfCounter)(nil)), + kind: reflect.TypeFor[*sfCounter](), description: cfg.Description(), unit: cfg.Unit(), } @@ -356,7 +356,7 @@ func (m *meter) Float64UpDownCounter( cfg := metric.NewFloat64UpDownCounterConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*sfUpDownCounter)(nil)), + kind: reflect.TypeFor[*sfUpDownCounter](), description: cfg.Description(), unit: cfg.Unit(), } @@ -382,7 +382,7 @@ func (m *meter) Float64Histogram( cfg := metric.NewFloat64HistogramConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*sfHistogram)(nil)), + kind: reflect.TypeFor[*sfHistogram](), description: cfg.Description(), unit: cfg.Unit(), } @@ -405,7 +405,7 @@ func (m *meter) Float64Gauge(name string, options ...metric.Float64GaugeOption) cfg := metric.NewFloat64GaugeConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*sfGauge)(nil)), + kind: reflect.TypeFor[*sfGauge](), description: cfg.Description(), unit: cfg.Unit(), } @@ -431,7 +431,7 @@ func (m *meter) Float64ObservableCounter( cfg := metric.NewFloat64ObservableCounterConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*afCounter)(nil)), + kind: reflect.TypeFor[*afCounter](), description: cfg.Description(), unit: cfg.Unit(), } @@ -457,7 +457,7 @@ func (m *meter) Float64ObservableUpDownCounter( cfg := metric.NewFloat64ObservableUpDownCounterConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*afUpDownCounter)(nil)), + kind: reflect.TypeFor[*afUpDownCounter](), description: cfg.Description(), unit: cfg.Unit(), } @@ -483,7 +483,7 @@ func (m *meter) Float64ObservableGauge( cfg := metric.NewFloat64ObservableGaugeConfig(options...) id := instID{ name: name, - kind: reflect.TypeOf((*afGauge)(nil)), + kind: reflect.TypeFor[*afGauge](), description: cfg.Description(), unit: cfg.Unit(), } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric.go index 1e6473b32f..527d9aec86 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric.go @@ -11,7 +11,7 @@ import ( // Meter returns a Meter from the global MeterProvider. The name must be the // name of the library providing instrumentation. This name may be the same as // the instrumented code only if that code provides built-in instrumentation. -// If the name is empty, then a implementation defined default name will be +// If the name is empty, then an implementation defined default name will be // used instead. // // If this is called before a global MeterProvider is registered the returned diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go index b7fc973a66..eb4f5961fd 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go @@ -227,7 +227,11 @@ type Float64Observer interface { // attributes as another Float64Callbacks also registered for the same // instrument. // -// The function needs to be concurrent safe. +// The function needs to be reentrant and concurrent safe. +// +// Note that Go's mutexes are not reentrant, and locking a mutex takes +// an indefinite amount of time. It is therefore advised to avoid +// using mutexes inside callbacks. type Float64Callback func(context.Context, Float64Observer) error // Float64ObservableOption applies options to float64 Observer instruments. diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/asyncint64.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/asyncint64.go index 4404b71a22..1dfc4b0f25 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/asyncint64.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/asyncint64.go @@ -225,7 +225,11 @@ type Int64Observer interface { // attributes as another Int64Callbacks also registered for the same // instrument. // -// The function needs to be concurrent safe. +// The function needs to be reentrant and concurrent safe. +// +// Note that Go's mutexes are not reentrant, and locking a mutex takes +// an indefinite amount of time. It is therefore advised to avoid +// using mutexes inside callbacks. type Int64Callback func(context.Context, Int64Observer) error // Int64ObservableOption applies options to int64 Observer instruments. diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/config.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/config.go index d9e3b13e4d..e42dd6e70a 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/config.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/config.go @@ -3,7 +3,11 @@ package metric // import "go.opentelemetry.io/otel/metric" -import "go.opentelemetry.io/otel/attribute" +import ( + "slices" + + "go.opentelemetry.io/otel/attribute" +) // MeterConfig contains options for Meters. type MeterConfig struct { @@ -62,12 +66,38 @@ func WithInstrumentationVersion(version string) MeterOption { }) } -// WithInstrumentationAttributes sets the instrumentation attributes. +// WithInstrumentationAttributes adds the instrumentation attributes. +// +// This is equivalent to calling [WithInstrumentationAttributeSet] with an +// [attribute.Set] created from a clone of the passed attributes. +// [WithInstrumentationAttributeSet] is recommended for more control. // -// The passed attributes will be de-duplicated. +// If multiple [WithInstrumentationAttributes] or [WithInstrumentationAttributeSet] +// options are passed, the attributes will be merged together in the order +// they are passed. Attributes with duplicate keys will use the last value passed. func WithInstrumentationAttributes(attr ...attribute.KeyValue) MeterOption { + set := attribute.NewSet(slices.Clone(attr)...) + return WithInstrumentationAttributeSet(set) +} + +// WithInstrumentationAttributeSet adds the instrumentation attributes. +// +// If multiple [WithInstrumentationAttributes] or [WithInstrumentationAttributeSet] +// options are passed, the attributes will be merged together in the order +// they are passed. Attributes with duplicate keys will use the last value passed. +func WithInstrumentationAttributeSet(set attribute.Set) MeterOption { + if set.Len() == 0 { + return meterOptionFunc(func(config MeterConfig) MeterConfig { + return config + }) + } + return meterOptionFunc(func(config MeterConfig) MeterConfig { - config.attrs = attribute.NewSet(attr...) + if config.attrs.Len() == 0 { + config.attrs = set + } else { + config.attrs = mergeSets(config.attrs, set) + } return config }) } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/meter.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/meter.go index fdd2a7011c..a16c4c0a14 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/meter.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/meter.go @@ -244,7 +244,11 @@ type Meter interface { // Callbacks. Meaning, it should not report measurements for an instrument with // the same attributes as another Callback will report. // -// The function needs to be concurrent safe. +// The function needs to be reentrant and concurrent safe. +// +// Note that Go's mutexes are not reentrant, and locking a mutex takes +// an indefinite amount of time. It is therefore advised to avoid +// using mutexes inside callbacks. type Callback func(context.Context, Observer) error // Observer records measurements for multiple instruments in a Callback. diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/noop/noop.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/noop/noop.go index 9afb69e583..634e73aee0 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/noop/noop.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/noop/noop.go @@ -191,6 +191,9 @@ type Int64Counter struct{ embedded.Int64Counter } // Add performs no operation. func (Int64Counter) Add(context.Context, int64, ...metric.AddOption) {} +// Enabled performs no operation. +func (Int64Counter) Enabled(context.Context) bool { return false } + // Float64Counter is an OpenTelemetry Counter used to record float64 // measurements. It produces no telemetry. type Float64Counter struct{ embedded.Float64Counter } @@ -198,6 +201,9 @@ type Float64Counter struct{ embedded.Float64Counter } // Add performs no operation. func (Float64Counter) Add(context.Context, float64, ...metric.AddOption) {} +// Enabled performs no operation. +func (Float64Counter) Enabled(context.Context) bool { return false } + // Int64UpDownCounter is an OpenTelemetry UpDownCounter used to record int64 // measurements. It produces no telemetry. type Int64UpDownCounter struct{ embedded.Int64UpDownCounter } @@ -205,6 +211,9 @@ type Int64UpDownCounter struct{ embedded.Int64UpDownCounter } // Add performs no operation. func (Int64UpDownCounter) Add(context.Context, int64, ...metric.AddOption) {} +// Enabled performs no operation. +func (Int64UpDownCounter) Enabled(context.Context) bool { return false } + // Float64UpDownCounter is an OpenTelemetry UpDownCounter used to record // float64 measurements. It produces no telemetry. type Float64UpDownCounter struct{ embedded.Float64UpDownCounter } @@ -212,6 +221,9 @@ type Float64UpDownCounter struct{ embedded.Float64UpDownCounter } // Add performs no operation. func (Float64UpDownCounter) Add(context.Context, float64, ...metric.AddOption) {} +// Enabled performs no operation. +func (Float64UpDownCounter) Enabled(context.Context) bool { return false } + // Int64Histogram is an OpenTelemetry Histogram used to record int64 // measurements. It produces no telemetry. type Int64Histogram struct{ embedded.Int64Histogram } @@ -219,6 +231,9 @@ type Int64Histogram struct{ embedded.Int64Histogram } // Record performs no operation. func (Int64Histogram) Record(context.Context, int64, ...metric.RecordOption) {} +// Enabled performs no operation. +func (Int64Histogram) Enabled(context.Context) bool { return false } + // Float64Histogram is an OpenTelemetry Histogram used to record float64 // measurements. It produces no telemetry. type Float64Histogram struct{ embedded.Float64Histogram } @@ -226,6 +241,9 @@ type Float64Histogram struct{ embedded.Float64Histogram } // Record performs no operation. func (Float64Histogram) Record(context.Context, float64, ...metric.RecordOption) {} +// Enabled performs no operation. +func (Float64Histogram) Enabled(context.Context) bool { return false } + // Int64Gauge is an OpenTelemetry Gauge used to record instantaneous int64 // measurements. It produces no telemetry. type Int64Gauge struct{ embedded.Int64Gauge } @@ -233,6 +251,9 @@ type Int64Gauge struct{ embedded.Int64Gauge } // Record performs no operation. func (Int64Gauge) Record(context.Context, int64, ...metric.RecordOption) {} +// Enabled performs no operation. +func (Int64Gauge) Enabled(context.Context) bool { return false } + // Float64Gauge is an OpenTelemetry Gauge used to record instantaneous float64 // measurements. It produces no telemetry. type Float64Gauge struct{ embedded.Float64Gauge } @@ -240,6 +261,9 @@ type Float64Gauge struct{ embedded.Float64Gauge } // Record performs no operation. func (Float64Gauge) Record(context.Context, float64, ...metric.RecordOption) {} +// Enabled performs no operation. +func (Float64Gauge) Enabled(context.Context) bool { return false } + // Int64ObservableCounter is an OpenTelemetry ObservableCounter used to record // int64 measurements. It produces no telemetry. type Int64ObservableCounter struct { diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/syncfloat64.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/syncfloat64.go index 8403a4bad2..57a74c5e65 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/syncfloat64.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/syncfloat64.go @@ -25,6 +25,12 @@ type Float64Counter interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Add(ctx context.Context, incr float64, options ...AddOption) + + // Enabled reports whether the instrument will process measurements for the given context. + // + // This function can be used in places where measuring an instrument + // would result in computationally expensive operations. + Enabled(context.Context) bool } // Float64CounterConfig contains options for synchronous counter instruments that @@ -78,6 +84,12 @@ type Float64UpDownCounter interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Add(ctx context.Context, incr float64, options ...AddOption) + + // Enabled reports whether the instrument will process measurements for the given context. + // + // This function can be used in places where measuring an instrument + // would result in computationally expensive operations. + Enabled(context.Context) bool } // Float64UpDownCounterConfig contains options for synchronous counter @@ -131,6 +143,12 @@ type Float64Histogram interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Record(ctx context.Context, incr float64, options ...RecordOption) + + // Enabled reports whether the instrument will process measurements for the given context. + // + // This function can be used in places where measuring an instrument + // would result in computationally expensive operations. + Enabled(context.Context) bool } // Float64HistogramConfig contains options for synchronous histogram @@ -189,6 +207,12 @@ type Float64Gauge interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Record(ctx context.Context, value float64, options ...RecordOption) + + // Enabled reports whether the instrument will process measurements for the given context. + // + // This function can be used in places where measuring an instrument + // would result in computationally expensive operations. + Enabled(context.Context) bool } // Float64GaugeConfig contains options for synchronous gauge instruments that diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/syncint64.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/syncint64.go index 783fdfba77..ac2d033ea6 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/syncint64.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/metric/syncint64.go @@ -25,6 +25,12 @@ type Int64Counter interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Add(ctx context.Context, incr int64, options ...AddOption) + + // Enabled reports whether the instrument will process measurements for the given context. + // + // This function can be used in places where measuring an instrument + // would result in computationally expensive operations. + Enabled(context.Context) bool } // Int64CounterConfig contains options for synchronous counter instruments that @@ -78,6 +84,12 @@ type Int64UpDownCounter interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Add(ctx context.Context, incr int64, options ...AddOption) + + // Enabled reports whether the instrument will process measurements for the given context. + // + // This function can be used in places where measuring an instrument + // would result in computationally expensive operations. + Enabled(context.Context) bool } // Int64UpDownCounterConfig contains options for synchronous counter @@ -131,6 +143,12 @@ type Int64Histogram interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Record(ctx context.Context, incr int64, options ...RecordOption) + + // Enabled reports whether the instrument will process measurements for the given context. + // + // This function can be used in places where measuring an instrument + // would result in computationally expensive operations. + Enabled(context.Context) bool } // Int64HistogramConfig contains options for synchronous histogram instruments @@ -189,6 +207,12 @@ type Int64Gauge interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Record(ctx context.Context, value int64, options ...RecordOption) + + // Enabled reports whether the instrument will process measurements for the given context. + // + // This function can be used in places where measuring an instrument + // would result in computationally expensive operations. + Enabled(context.Context) bool } // Int64GaugeConfig contains options for synchronous gauge instruments that diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/propagation/trace_context.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/propagation/trace_context.go index 6692d2665d..271ab71f1a 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/propagation/trace_context.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/propagation/trace_context.go @@ -111,7 +111,7 @@ func (TraceContext) extract(carrier TextMapCarrier) trace.SpanContext { } // Clear all flags other than the trace-context supported sampling bit. - scc.TraceFlags = trace.TraceFlags(opts[0]) & trace.FlagsSampled + scc.TraceFlags = trace.TraceFlags(opts[0]) & trace.FlagsSampled // nolint:gosec // slice size already checked. // Ignore the error returned here. Failure to parse tracestate MUST NOT // affect the parsing of traceparent according to the W3C tracecontext diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go new file mode 100644 index 0000000000..bfeb73e811 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package x documents experimental features for [go.opentelemetry.io/otel/sdk]. +package x // import "go.opentelemetry.io/otel/sdk/internal/x" + +import "strings" + +// Resource is an experimental feature flag that defines if resource detectors +// should be included experimental semantic conventions. +// +// To enable this feature set the OTEL_GO_X_RESOURCE environment variable +// to the case-insensitive string value of "true" (i.e. "True" and "TRUE" +// will also enable this). +var Resource = newFeature( + []string{"RESOURCE"}, + func(v string) (string, bool) { + if strings.EqualFold(v, "true") { + return v, true + } + return "", false + }, +) + +// Observability is an experimental feature flag that determines if SDK +// observability metrics are enabled. +// +// To enable this feature set the OTEL_GO_X_OBSERVABILITY environment variable +// to the case-insensitive string value of "true" (i.e. "True" and "TRUE" +// will also enable this). +var Observability = newFeature( + []string{"OBSERVABILITY", "SELF_OBSERVABILITY"}, + func(v string) (string, bool) { + if strings.EqualFold(v, "true") { + return v, true + } + return "", false + }, +) diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/internal/x/x.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/internal/x/x.go index 1be472e917..13347e5605 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/internal/x/x.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/internal/x/x.go @@ -1,48 +1,38 @@ +// Code generated by gotmpl. DO NOT MODIFY. +// source: internal/shared/x/x.go.tmpl + // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -// Package x contains support for OTel SDK experimental features. -// -// This package should only be used for features defined in the specification. -// It should not be used for experiments or new project ideas. +// Package x documents experimental features for [go.opentelemetry.io/otel/sdk]. package x // import "go.opentelemetry.io/otel/sdk/internal/x" import ( "os" - "strings" ) -// Resource is an experimental feature flag that defines if resource detectors -// should be included experimental semantic conventions. -// -// To enable this feature set the OTEL_GO_X_RESOURCE environment variable -// to the case-insensitive string value of "true" (i.e. "True" and "TRUE" -// will also enable this). -var Resource = newFeature("RESOURCE", func(v string) (string, bool) { - if strings.EqualFold(v, "true") { - return v, true - } - return "", false -}) - // Feature is an experimental feature control flag. It provides a uniform way // to interact with these feature flags and parse their values. type Feature[T any] struct { - key string + keys []string parse func(v string) (T, bool) } -func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] { +func newFeature[T any](suffix []string, parse func(string) (T, bool)) Feature[T] { const envKeyRoot = "OTEL_GO_X_" + keys := make([]string, 0, len(suffix)) + for _, s := range suffix { + keys = append(keys, envKeyRoot+s) + } return Feature[T]{ - key: envKeyRoot + suffix, + keys: keys, parse: parse, } } -// Key returns the environment variable key that needs to be set to enable the +// Keys returns the environment variable keys that can be set to enable the // feature. -func (f Feature[T]) Key() string { return f.key } +func (f Feature[T]) Keys() []string { return f.keys } // Lookup returns the user configured value for the feature and true if the // user has enabled the feature. Otherwise, if the feature is not enabled, a @@ -52,11 +42,13 @@ func (f Feature[T]) Lookup() (v T, ok bool) { // // > The SDK MUST interpret an empty value of an environment variable the // > same way as when the variable is unset. - vRaw := os.Getenv(f.key) - if vRaw == "" { - return v, ok + for _, key := range f.keys { + vRaw := os.Getenv(key) + if vRaw != "" { + return f.parse(vRaw) + } } - return f.parse(vRaw) + return v, ok } // Enabled reports whether the feature is enabled. diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go index 3f20eb7a56..8a7bb330bf 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go @@ -13,7 +13,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) type ( diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/container.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/container.go index bbe142d203..a19b39def8 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/container.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/container.go @@ -11,7 +11,7 @@ import ( "os" "regexp" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) type containerIDProvider func() (string, error) diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/env.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/env.go index 4a1b017eea..c49157224e 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/env.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/env.go @@ -12,7 +12,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) const ( diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go index 5fed33d4fb..023621ba76 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go @@ -8,7 +8,7 @@ import ( "errors" "strings" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) type hostIDProvider func() (string, error) @@ -51,17 +51,16 @@ type hostIDReaderDarwin struct { execCommand commandExecutor } -// read executes `ioreg -rd1 -c "IOPlatformExpertDevice"` and parses host id +// read executes `/usr/sbin/ioreg -rd1 -c "IOPlatformExpertDevice"` and parses host id // from the IOPlatformUUID line. If the command fails or the uuid cannot be // parsed an error will be returned. func (r *hostIDReaderDarwin) read() (string, error) { - result, err := r.execCommand("ioreg", "-rd1", "-c", "IOPlatformExpertDevice") + result, err := r.execCommand("/usr/sbin/ioreg", "-rd1", "-c", "IOPlatformExpertDevice") if err != nil { return "", err } - lines := strings.Split(result, "\n") - for _, line := range lines { + for line := range strings.SplitSeq(result, "\n") { if strings.Contains(line, "IOPlatformUUID") { parts := strings.Split(line, " = ") if len(parts) == 2 { diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_bsd.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_bsd.go index cc8b8938ed..4c1c30f256 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_bsd.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_bsd.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build dragonfly || freebsd || netbsd || openbsd || solaris -// +build dragonfly freebsd netbsd openbsd solaris package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_linux.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_linux.go index f84f173240..4a26096c8d 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_linux.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_linux.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build linux -// +build linux package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_unsupported.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_unsupported.go index df12c44c56..63ad2fa4e0 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_unsupported.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_unsupported.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows -// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_windows.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_windows.go index 3677c83d7d..2b8ca20b38 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_windows.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_windows.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build windows -// +build windows package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os.go index 51da76e807..534809e217 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os.go @@ -8,7 +8,7 @@ import ( "strings" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) type osDescriptionProvider func() (string, error) diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go index 7252af79fc..a1763267c2 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build aix || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix dragonfly freebsd linux netbsd openbsd solaris zos package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go index a6ff26a4d2..6c50ab6867 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go index a77742b077..25f629532a 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/process.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/process.go index 138e57721b..a1189553c7 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/process.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/resource/process.go @@ -11,7 +11,7 @@ import ( "path/filepath" "runtime" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) type ( diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go index 9bc3e525d1..7d15cbb9c0 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go @@ -6,20 +6,14 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" "errors" - "fmt" "sync" "sync/atomic" "time" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/internal/global" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/sdk" - "go.opentelemetry.io/otel/sdk/internal/env" - "go.opentelemetry.io/otel/sdk/trace/internal/x" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" - "go.opentelemetry.io/otel/semconv/v1.37.0/otelconv" + "go.opentelemetry.io/otel/sdk/trace/internal/env" + "go.opentelemetry.io/otel/sdk/trace/internal/observ" "go.opentelemetry.io/otel/trace" ) @@ -33,8 +27,6 @@ const ( DefaultMaxExportBatchSize = 512 ) -var queueFull = otelconv.ErrorTypeAttr("queue_full") - // BatchSpanProcessorOption configures a BatchSpanProcessor. type BatchSpanProcessorOption func(o *BatchSpanProcessorOptions) @@ -78,10 +70,7 @@ type batchSpanProcessor struct { queue chan ReadOnlySpan dropped uint32 - selfObservabilityEnabled bool - callbackRegistration metric.Registration - spansProcessedCounter otelconv.SDKProcessorSpanProcessed - componentNameAttr attribute.KeyValue + inst *observ.BSP batch []ReadOnlySpan batchMutex sync.Mutex @@ -124,19 +113,14 @@ func NewBatchSpanProcessor(exporter SpanExporter, options ...BatchSpanProcessorO stopCh: make(chan struct{}), } - if x.SelfObservability.Enabled() { - bsp.selfObservabilityEnabled = true - bsp.componentNameAttr = componentName() - - var err error - bsp.spansProcessedCounter, bsp.callbackRegistration, err = newBSPObs( - bsp.componentNameAttr, - func() int64 { return int64(len(bsp.queue)) }, - int64(bsp.o.MaxQueueSize), - ) - if err != nil { - otel.Handle(err) - } + var err error + bsp.inst, err = observ.NewBSP( + nextProcessorID(), + func() int64 { return int64(len(bsp.queue)) }, + int64(bsp.o.MaxQueueSize), + ) + if err != nil { + otel.Handle(err) } bsp.stopWait.Add(1) @@ -157,51 +141,6 @@ func nextProcessorID() int64 { return processorIDCounter.Add(1) - 1 } -func componentName() attribute.KeyValue { - id := nextProcessorID() - name := fmt.Sprintf("%s/%d", otelconv.ComponentTypeBatchingSpanProcessor, id) - return semconv.OTelComponentName(name) -} - -// newBSPObs creates and returns a new set of metrics instruments and a -// registration for a BatchSpanProcessor. It is the caller's responsibility -// to unregister the registration when it is no longer needed. -func newBSPObs( - cmpnt attribute.KeyValue, - qLen func() int64, - qMax int64, -) (otelconv.SDKProcessorSpanProcessed, metric.Registration, error) { - meter := otel.GetMeterProvider().Meter( - selfObsScopeName, - metric.WithInstrumentationVersion(sdk.Version()), - metric.WithSchemaURL(semconv.SchemaURL), - ) - - qCap, err := otelconv.NewSDKProcessorSpanQueueCapacity(meter) - - qSize, e := otelconv.NewSDKProcessorSpanQueueSize(meter) - err = errors.Join(err, e) - - spansProcessed, e := otelconv.NewSDKProcessorSpanProcessed(meter) - err = errors.Join(err, e) - - cmpntT := semconv.OTelComponentTypeBatchingSpanProcessor - attrs := metric.WithAttributes(cmpnt, cmpntT) - - reg, e := meter.RegisterCallback( - func(_ context.Context, o metric.Observer) error { - o.ObserveInt64(qSize.Inst(), qLen(), attrs) - o.ObserveInt64(qCap.Inst(), qMax, attrs) - return nil - }, - qSize.Inst(), - qCap.Inst(), - ) - err = errors.Join(err, e) - - return spansProcessed, reg, err -} - // OnStart method does nothing. func (*batchSpanProcessor) OnStart(context.Context, ReadWriteSpan) {} @@ -242,8 +181,8 @@ func (bsp *batchSpanProcessor) Shutdown(ctx context.Context) error { case <-ctx.Done(): err = ctx.Err() } - if bsp.selfObservabilityEnabled { - err = errors.Join(err, bsp.callbackRegistration.Unregister()) + if bsp.inst != nil { + err = errors.Join(err, bsp.inst.Shutdown()) } }) return err @@ -357,10 +296,8 @@ func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error { if l := len(bsp.batch); l > 0 { global.Debug("exporting spans", "count", len(bsp.batch), "total_dropped", atomic.LoadUint32(&bsp.dropped)) - if bsp.selfObservabilityEnabled { - bsp.spansProcessedCounter.Add(ctx, int64(l), - bsp.componentNameAttr, - bsp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeBatchingSpanProcessor)) + if bsp.inst != nil { + bsp.inst.Processed(ctx, int64(l)) } err := bsp.e.ExportSpans(ctx, bsp.batch) @@ -470,11 +407,8 @@ func (bsp *batchSpanProcessor) enqueueBlockOnQueueFull(ctx context.Context, sd R case bsp.queue <- sd: return true case <-ctx.Done(): - if bsp.selfObservabilityEnabled { - bsp.spansProcessedCounter.Add(ctx, 1, - bsp.componentNameAttr, - bsp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeBatchingSpanProcessor), - bsp.spansProcessedCounter.AttrErrorType(queueFull)) + if bsp.inst != nil { + bsp.inst.ProcessedQueueFull(ctx, 1) } return false } @@ -490,11 +424,8 @@ func (bsp *batchSpanProcessor) enqueueDrop(ctx context.Context, sd ReadOnlySpan) return true default: atomic.AddUint32(&bsp.dropped, 1) - if bsp.selfObservabilityEnabled { - bsp.spansProcessedCounter.Add(ctx, 1, - bsp.componentNameAttr, - bsp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeBatchingSpanProcessor), - bsp.spansProcessedCounter.AttrErrorType(queueFull)) + if bsp.inst != nil { + bsp.inst.ProcessedQueueFull(ctx, 1) } } return false diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/doc.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/doc.go index e58e7f6ed7..b502c7d479 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/doc.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/doc.go @@ -7,7 +7,7 @@ Package trace contains support for OpenTelemetry distributed tracing. The following assumes a basic familiarity with OpenTelemetry concepts. See https://opentelemetry.io. -See [go.opentelemetry.io/otel/sdk/trace/internal/x] for information about +See [go.opentelemetry.io/otel/sdk/internal/x] for information about the experimental features. */ package trace // import "go.opentelemetry.io/otel/sdk/trace" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/env/env.go similarity index 98% rename from cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go rename to cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/env/env.go index e3309231d4..58f68df441 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/env/env.go @@ -3,7 +3,7 @@ // Package env provides types and functionality for environment variable support // in the OpenTelemetry SDK. -package env // import "go.opentelemetry.io/otel/sdk/internal/env" +package env // import "go.opentelemetry.io/otel/sdk/trace/internal/env" import ( "os" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go new file mode 100644 index 0000000000..d9cfba0b45 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go @@ -0,0 +1,119 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ" + +import ( + "context" + "errors" + "fmt" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/sdk" + "go.opentelemetry.io/otel/sdk/internal/x" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" + "go.opentelemetry.io/otel/semconv/v1.39.0/otelconv" +) + +const ( + // ScopeName is the name of the instrumentation scope. + ScopeName = "go.opentelemetry.io/otel/sdk/trace/internal/observ" + + // SchemaURL is the schema URL of the instrumentation. + SchemaURL = semconv.SchemaURL +) + +// ErrQueueFull is the attribute value for the "queue_full" error type. +var ErrQueueFull = otelconv.SDKProcessorSpanProcessed{}.AttrErrorType( + otelconv.ErrorTypeAttr("queue_full"), +) + +// BSPComponentName returns the component name attribute for a +// BatchSpanProcessor with the given ID. +func BSPComponentName(id int64) attribute.KeyValue { + t := otelconv.ComponentTypeBatchingSpanProcessor + name := fmt.Sprintf("%s/%d", t, id) + return semconv.OTelComponentName(name) +} + +// BSP is the instrumentation for an OTel SDK BatchSpanProcessor. +type BSP struct { + reg metric.Registration + + processed metric.Int64Counter + processedOpts []metric.AddOption + processedQueueFullOpts []metric.AddOption +} + +func NewBSP(id int64, qLen func() int64, qMax int64) (*BSP, error) { + if !x.Observability.Enabled() { + return nil, nil + } + + meter := otel.GetMeterProvider().Meter( + ScopeName, + metric.WithInstrumentationVersion(sdk.Version()), + metric.WithSchemaURL(SchemaURL), + ) + + qCap, err := otelconv.NewSDKProcessorSpanQueueCapacity(meter) + if err != nil { + err = fmt.Errorf("failed to create BSP queue capacity metric: %w", err) + } + qCapInst := qCap.Inst() + + qSize, e := otelconv.NewSDKProcessorSpanQueueSize(meter) + if e != nil { + e := fmt.Errorf("failed to create BSP queue size metric: %w", e) + err = errors.Join(err, e) + } + qSizeInst := qSize.Inst() + + cmpntT := semconv.OTelComponentTypeBatchingSpanProcessor + cmpnt := BSPComponentName(id) + set := attribute.NewSet(cmpnt, cmpntT) + + obsOpts := []metric.ObserveOption{metric.WithAttributeSet(set)} + reg, e := meter.RegisterCallback( + func(_ context.Context, o metric.Observer) error { + o.ObserveInt64(qSizeInst, qLen(), obsOpts...) + o.ObserveInt64(qCapInst, qMax, obsOpts...) + return nil + }, + qSizeInst, + qCapInst, + ) + if e != nil { + e := fmt.Errorf("failed to register BSP queue size/capacity callback: %w", e) + err = errors.Join(err, e) + } + + processed, e := otelconv.NewSDKProcessorSpanProcessed(meter) + if e != nil { + e := fmt.Errorf("failed to create BSP processed spans metric: %w", e) + err = errors.Join(err, e) + } + processedOpts := []metric.AddOption{metric.WithAttributeSet(set)} + + set = attribute.NewSet(cmpnt, cmpntT, ErrQueueFull) + processedQueueFullOpts := []metric.AddOption{metric.WithAttributeSet(set)} + + return &BSP{ + reg: reg, + processed: processed.Inst(), + processedOpts: processedOpts, + processedQueueFullOpts: processedQueueFullOpts, + }, err +} + +func (b *BSP) Shutdown() error { return b.reg.Unregister() } + +func (b *BSP) Processed(ctx context.Context, n int64) { + b.processed.Add(ctx, n, b.processedOpts...) +} + +func (b *BSP) ProcessedQueueFull(ctx context.Context, n int64) { + b.processed.Add(ctx, n, b.processedQueueFullOpts...) +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/doc.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/doc.go new file mode 100644 index 0000000000..b542121e6a --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/doc.go @@ -0,0 +1,6 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package observ provides observability instrumentation for the OTel trace SDK +// package. +package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go new file mode 100644 index 0000000000..8afd052679 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go @@ -0,0 +1,97 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ" + +import ( + "context" + "fmt" + "sync" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/sdk" + "go.opentelemetry.io/otel/sdk/internal/x" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" + "go.opentelemetry.io/otel/semconv/v1.39.0/otelconv" +) + +var measureAttrsPool = sync.Pool{ + New: func() any { + // "component.name" + "component.type" + "error.type" + const n = 1 + 1 + 1 + s := make([]attribute.KeyValue, 0, n) + // Return a pointer to a slice instead of a slice itself + // to avoid allocations on every call. + return &s + }, +} + +// SSP is the instrumentation for an OTel SDK SimpleSpanProcessor. +type SSP struct { + spansProcessedCounter metric.Int64Counter + addOpts []metric.AddOption + attrs []attribute.KeyValue +} + +// SSPComponentName returns the component name attribute for a +// SimpleSpanProcessor with the given ID. +func SSPComponentName(id int64) attribute.KeyValue { + t := otelconv.ComponentTypeSimpleSpanProcessor + name := fmt.Sprintf("%s/%d", t, id) + return semconv.OTelComponentName(name) +} + +// NewSSP returns instrumentation for an OTel SDK SimpleSpanProcessor with the +// provided ID. +// +// If the experimental observability is disabled, nil is returned. +func NewSSP(id int64) (*SSP, error) { + if !x.Observability.Enabled() { + return nil, nil + } + + meter := otel.GetMeterProvider().Meter( + ScopeName, + metric.WithInstrumentationVersion(sdk.Version()), + metric.WithSchemaURL(SchemaURL), + ) + spansProcessedCounter, err := otelconv.NewSDKProcessorSpanProcessed(meter) + if err != nil { + err = fmt.Errorf("failed to create SSP processed spans metric: %w", err) + } + + componentName := SSPComponentName(id) + componentType := spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeSimpleSpanProcessor) + attrs := []attribute.KeyValue{componentName, componentType} + addOpts := []metric.AddOption{metric.WithAttributeSet(attribute.NewSet(attrs...))} + + return &SSP{ + spansProcessedCounter: spansProcessedCounter.Inst(), + addOpts: addOpts, + attrs: attrs, + }, err +} + +// SpanProcessed records that a span has been processed by the SimpleSpanProcessor. +// If err is non-nil, it records the processing error as an attribute. +func (ssp *SSP) SpanProcessed(ctx context.Context, err error) { + ssp.spansProcessedCounter.Add(ctx, 1, ssp.addOption(err)...) +} + +func (ssp *SSP) addOption(err error) []metric.AddOption { + if err == nil { + return ssp.addOpts + } + attrs := measureAttrsPool.Get().(*[]attribute.KeyValue) + defer func() { + *attrs = (*attrs)[:0] // reset the slice for reuse + measureAttrsPool.Put(attrs) + }() + *attrs = append(*attrs, ssp.attrs...) + *attrs = append(*attrs, semconv.ErrorType(err)) + // Do not inefficiently make a copy of attrs by using + // WithAttributes instead of WithAttributeSet. + return []metric.AddOption{metric.WithAttributeSet(attribute.NewSet(*attrs...))} +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go new file mode 100644 index 0000000000..13a2db2969 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go @@ -0,0 +1,223 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ" + +import ( + "context" + "errors" + "fmt" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/sdk" + "go.opentelemetry.io/otel/sdk/internal/x" + "go.opentelemetry.io/otel/semconv/v1.39.0/otelconv" + "go.opentelemetry.io/otel/trace" +) + +var meterOpts = []metric.MeterOption{ + metric.WithInstrumentationVersion(sdk.Version()), + metric.WithSchemaURL(SchemaURL), +} + +// Tracer is instrumentation for an OTel SDK Tracer. +type Tracer struct { + enabled bool + + live metric.Int64UpDownCounter + started metric.Int64Counter +} + +func NewTracer() (Tracer, error) { + if !x.Observability.Enabled() { + return Tracer{}, nil + } + meter := otel.GetMeterProvider().Meter(ScopeName, meterOpts...) + + var err error + l, e := otelconv.NewSDKSpanLive(meter) + if e != nil { + e = fmt.Errorf("failed to create span live metric: %w", e) + err = errors.Join(err, e) + } + + s, e := otelconv.NewSDKSpanStarted(meter) + if e != nil { + e = fmt.Errorf("failed to create span started metric: %w", e) + err = errors.Join(err, e) + } + + return Tracer{enabled: true, live: l.Inst(), started: s.Inst()}, err +} + +func (t Tracer) Enabled() bool { return t.enabled } + +func (t Tracer) SpanStarted(ctx context.Context, psc trace.SpanContext, span trace.Span) { + key := spanStartedKey{ + parent: parentStateNoParent, + sampling: samplingStateDrop, + } + + if psc.IsValid() { + if psc.IsRemote() { + key.parent = parentStateRemoteParent + } else { + key.parent = parentStateLocalParent + } + } + + if span.IsRecording() { + if span.SpanContext().IsSampled() { + key.sampling = samplingStateRecordAndSample + } else { + key.sampling = samplingStateRecordOnly + } + } + + opts := spanStartedOpts[key] + t.started.Add(ctx, 1, opts...) +} + +func (t Tracer) SpanLive(ctx context.Context, span trace.Span) { + t.spanLive(ctx, 1, span) +} + +func (t Tracer) SpanEnded(ctx context.Context, span trace.Span) { + t.spanLive(ctx, -1, span) +} + +func (t Tracer) spanLive(ctx context.Context, value int64, span trace.Span) { + key := spanLiveKey{sampled: span.SpanContext().IsSampled()} + opts := spanLiveOpts[key] + t.live.Add(ctx, value, opts...) +} + +type parentState int + +const ( + parentStateNoParent parentState = iota + parentStateLocalParent + parentStateRemoteParent +) + +type samplingState int + +const ( + samplingStateDrop samplingState = iota + samplingStateRecordOnly + samplingStateRecordAndSample +) + +type spanStartedKey struct { + parent parentState + sampling samplingState +} + +var spanStartedOpts = map[spanStartedKey][]metric.AddOption{ + { + parentStateNoParent, + samplingStateDrop, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop), + )), + }, + { + parentStateLocalParent, + samplingStateDrop, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop), + )), + }, + { + parentStateRemoteParent, + samplingStateDrop, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop), + )), + }, + + { + parentStateNoParent, + samplingStateRecordOnly, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly), + )), + }, + { + parentStateLocalParent, + samplingStateRecordOnly, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly), + )), + }, + { + parentStateRemoteParent, + samplingStateRecordOnly, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly), + )), + }, + + { + parentStateNoParent, + samplingStateRecordAndSample, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample), + )), + }, + { + parentStateLocalParent, + samplingStateRecordAndSample, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample), + )), + }, + { + parentStateRemoteParent, + samplingStateRecordAndSample, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample), + )), + }, +} + +type spanLiveKey struct { + sampled bool +} + +var spanLiveOpts = map[spanLiveKey][]metric.AddOption{ + {true}: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanLive{}.AttrSpanSamplingResult( + otelconv.SpanSamplingResultRecordAndSample, + ), + )), + }, + {false}: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanLive{}.AttrSpanSamplingResult( + otelconv.SpanSamplingResultRecordOnly, + ), + )), + }, +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go index 37ce2ac876..d2cf4ebd3e 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go @@ -5,29 +5,21 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" - "errors" "fmt" "sync" "sync/atomic" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/internal/global" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/sdk" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" - "go.opentelemetry.io/otel/sdk/trace/internal/x" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" - "go.opentelemetry.io/otel/semconv/v1.37.0/otelconv" + "go.opentelemetry.io/otel/sdk/trace/internal/observ" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace/embedded" "go.opentelemetry.io/otel/trace/noop" ) -const ( - defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer" - selfObsScopeName = "go.opentelemetry.io/otel/sdk/trace" -) +const defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer" // tracerProviderConfig. type tracerProviderConfig struct { @@ -163,19 +155,16 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T t, ok := p.namedTracer[is] if !ok { t = &tracer{ - provider: p, - instrumentationScope: is, - selfObservabilityEnabled: x.SelfObservability.Enabled(), + provider: p, + instrumentationScope: is, } - if t.selfObservabilityEnabled { - var err error - t.spanLiveMetric, t.spanStartedMetric, err = newInst() - if err != nil { - msg := "failed to create self-observability metrics for tracer: %w" - err := fmt.Errorf(msg, err) - otel.Handle(err) - } + + var err error + t.inst, err = observ.NewTracer() + if err != nil { + otel.Handle(err) } + p.namedTracer[is] = t } return t, ok @@ -201,23 +190,6 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T return t } -func newInst() (otelconv.SDKSpanLive, otelconv.SDKSpanStarted, error) { - m := otel.GetMeterProvider().Meter( - selfObsScopeName, - metric.WithInstrumentationVersion(sdk.Version()), - metric.WithSchemaURL(semconv.SchemaURL), - ) - - var err error - spanLiveMetric, e := otelconv.NewSDKSpanLive(m) - err = errors.Join(err, e) - - spanStartedMetric, e := otelconv.NewSDKSpanStarted(m) - err = errors.Join(err, e) - - return spanLiveMetric, spanStartedMetric, err -} - // RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors. func (p *TracerProvider) RegisterSpanProcessor(sp SpanProcessor) { // This check prevents calls during a shutdown. diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go index 689663d48b..81c5060ad6 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go @@ -280,3 +280,31 @@ func (pb parentBased) Description() string { pb.config.localParentNotSampled.Description(), ) } + +// AlwaysRecord returns a sampler decorator which ensures that every span +// is passed to the SpanProcessor, even those that would be normally dropped. +// It converts `Drop` decisions from the root sampler into `RecordOnly` decisions, +// allowing processors to see all spans without sending them to exporters. This is +// typically used to enable accurate span-to-metrics processing. +func AlwaysRecord(root Sampler) Sampler { + return alwaysRecord{root} +} + +type alwaysRecord struct { + root Sampler +} + +func (ar alwaysRecord) ShouldSample(p SamplingParameters) SamplingResult { + rootSamplerSamplingResult := ar.root.ShouldSample(p) + if rootSamplerSamplingResult.Decision == Drop { + return SamplingResult{ + Decision: RecordOnly, + Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(), + } + } + return rootSamplerSamplingResult +} + +func (ar alwaysRecord) Description() string { + return "AlwaysRecord{root:" + ar.root.Description() + "}" +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go index 411d9ccdd7..771e427a4c 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go @@ -6,9 +6,12 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" "sync" + "sync/atomic" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/internal/global" + "go.opentelemetry.io/otel/sdk/trace/internal/observ" + "go.opentelemetry.io/otel/trace" ) // simpleSpanProcessor is a SpanProcessor that synchronously sends all @@ -17,6 +20,8 @@ type simpleSpanProcessor struct { exporterMu sync.Mutex exporter SpanExporter stopOnce sync.Once + + inst *observ.SSP } var _ SpanProcessor = (*simpleSpanProcessor)(nil) @@ -33,11 +38,26 @@ func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessor { ssp := &simpleSpanProcessor{ exporter: exporter, } + + var err error + ssp.inst, err = observ.NewSSP(nextSimpleProcessorID()) + if err != nil { + otel.Handle(err) + } + global.Warn("SimpleSpanProcessor is not recommended for production use, consider using BatchSpanProcessor instead.") return ssp } +var simpleProcessorIDCounter atomic.Int64 + +// nextSimpleProcessorID returns an identifier for this simple span processor, +// starting with 0 and incrementing by 1 each time it is called. +func nextSimpleProcessorID() int64 { + return simpleProcessorIDCounter.Add(1) - 1 +} + // OnStart does nothing. func (*simpleSpanProcessor) OnStart(context.Context, ReadWriteSpan) {} @@ -46,11 +66,20 @@ func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { ssp.exporterMu.Lock() defer ssp.exporterMu.Unlock() + var err error if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() { - if err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}); err != nil { + err = ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}) + if err != nil { otel.Handle(err) } } + + if ssp.inst != nil { + // Add the span to the context to ensure the metric is recorded + // with the correct span context. + ctx := trace.ContextWithSpanContext(context.Background(), s.SpanContext()) + ssp.inst.SpanProcessed(ctx, err) + } } // Shutdown shuts down the exporter this SimpleSpanProcessor exports to. diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/span.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/span.go index b376051fbb..d466610591 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/span.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/span.go @@ -20,7 +20,7 @@ import ( "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace/embedded" ) @@ -151,6 +151,12 @@ type recordingSpan struct { // tracer is the SDK tracer that created this span. tracer *tracer + + // origCtx is the context used when starting this span that has the + // recordingSpan instance set as the active span. If not nil, it is used + // when ending the span to ensure any metrics are recorded with a context + // containing this span without requiring an additional allocation. + origCtx context.Context } var ( @@ -158,6 +164,10 @@ var ( _ runtimeTracer = (*recordingSpan)(nil) ) +func (s *recordingSpan) setOrigCtx(ctx context.Context) { + s.origCtx = ctx +} + // SpanContext returns the SpanContext of this span. func (s *recordingSpan) SpanContext() trace.SpanContext { if s == nil { @@ -496,14 +506,15 @@ func (s *recordingSpan) End(options ...trace.SpanEndOption) { } s.mu.Unlock() - if s.tracer.selfObservabilityEnabled { - defer func() { - // Add the span to the context to ensure the metric is recorded - // with the correct span context. - ctx := trace.ContextWithSpan(context.Background(), s) - set := spanLiveSet(s.spanContext.IsSampled()) - s.tracer.spanLiveMetric.AddSet(ctx, -1, set) - }() + if s.tracer.inst.Enabled() { + ctx := s.origCtx + if ctx == nil { + // This should not happen as the origCtx should be set, but + // ensure trace information is propagated in the case of an + // error. + ctx = trace.ContextWithSpan(context.Background(), s) + } + defer s.tracer.inst.SpanEnded(ctx, s) } sps := s.tracer.provider.getSpanProcessors() diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go index bec5e20978..321d974305 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go @@ -3,7 +3,7 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace" -import "go.opentelemetry.io/otel/sdk/internal/env" +import "go.opentelemetry.io/otel/sdk/trace/internal/env" const ( // DefaultAttributeValueLengthLimit is the default maximum allowed diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go index e965c4cce8..e1d08fd4d8 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go @@ -7,9 +7,8 @@ import ( "context" "time" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/instrumentation" - "go.opentelemetry.io/otel/semconv/v1.37.0/otelconv" + "go.opentelemetry.io/otel/sdk/trace/internal/observ" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace/embedded" ) @@ -20,9 +19,7 @@ type tracer struct { provider *TracerProvider instrumentationScope instrumentation.Scope - selfObservabilityEnabled bool - spanLiveMetric otelconv.SDKSpanLive - spanStartedMetric otelconv.SDKSpanStarted + inst observ.Tracer } var _ trace.Tracer = &tracer{} @@ -53,10 +50,17 @@ func (tr *tracer) Start( s := tr.newSpan(ctx, name, &config) newCtx := trace.ContextWithSpan(ctx, s) - if tr.selfObservabilityEnabled { + if tr.inst.Enabled() { + if o, ok := s.(interface{ setOrigCtx(context.Context) }); ok { + // If this is a recording span, store the original context. + // This allows later retrieval of baggage and other information + // that may have been stored in the context at span start time and + // to avoid the allocation of repeatedly calling + // trace.ContextWithSpan. + o.setOrigCtx(newCtx) + } psc := trace.SpanContextFromContext(ctx) - set := spanStartedSet(psc, s) - tr.spanStartedMetric.AddSet(newCtx, 1, set) + tr.inst.SpanStarted(newCtx, psc, s) } if rw, ok := s.(ReadWriteSpan); ok && s.IsRecording() { @@ -168,12 +172,11 @@ func (tr *tracer) newRecordingSpan( s.SetAttributes(sr.Attributes...) s.SetAttributes(config.Attributes()...) - if tr.selfObservabilityEnabled { + if tr.inst.Enabled() { // Propagate any existing values from the context with the new span to // the measurement context. ctx = trace.ContextWithSpan(ctx, s) - set := spanLiveSet(s.spanContext.IsSampled()) - tr.spanLiveMetric.AddSet(ctx, 1, set) + tr.inst.SpanLive(ctx, s) } return s @@ -183,112 +186,3 @@ func (tr *tracer) newRecordingSpan( func (tr *tracer) newNonRecordingSpan(sc trace.SpanContext) nonRecordingSpan { return nonRecordingSpan{tracer: tr, sc: sc} } - -type parentState int - -const ( - parentStateNoParent parentState = iota - parentStateLocalParent - parentStateRemoteParent -) - -type samplingState int - -const ( - samplingStateDrop samplingState = iota - samplingStateRecordOnly - samplingStateRecordAndSample -) - -type spanStartedSetKey struct { - parent parentState - sampling samplingState -} - -var spanStartedSetCache = map[spanStartedSetKey]attribute.Set{ - {parentStateNoParent, samplingStateDrop}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop), - ), - {parentStateLocalParent, samplingStateDrop}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop), - ), - {parentStateRemoteParent, samplingStateDrop}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop), - ), - - {parentStateNoParent, samplingStateRecordOnly}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly), - ), - {parentStateLocalParent, samplingStateRecordOnly}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly), - ), - {parentStateRemoteParent, samplingStateRecordOnly}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly), - ), - - {parentStateNoParent, samplingStateRecordAndSample}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample), - ), - {parentStateLocalParent, samplingStateRecordAndSample}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample), - ), - {parentStateRemoteParent, samplingStateRecordAndSample}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample), - ), -} - -func spanStartedSet(psc trace.SpanContext, span trace.Span) attribute.Set { - key := spanStartedSetKey{ - parent: parentStateNoParent, - sampling: samplingStateDrop, - } - - if psc.IsValid() { - if psc.IsRemote() { - key.parent = parentStateRemoteParent - } else { - key.parent = parentStateLocalParent - } - } - - if span.IsRecording() { - if span.SpanContext().IsSampled() { - key.sampling = samplingStateRecordAndSample - } else { - key.sampling = samplingStateRecordOnly - } - } - - return spanStartedSetCache[key] -} - -type spanLiveSetKey struct { - sampled bool -} - -var spanLiveSetCache = map[spanLiveSetKey]attribute.Set{ - {true}: attribute.NewSet( - otelconv.SDKSpanLive{}.AttrSpanSamplingResult( - otelconv.SpanSamplingResultRecordAndSample, - ), - ), - {false}: attribute.NewSet( - otelconv.SDKSpanLive{}.AttrSpanSamplingResult( - otelconv.SpanSamplingResultRecordOnly, - ), - ), -} - -func spanLiveSet(sampled bool) attribute.Set { - key := spanLiveSetKey{sampled: sampled} - return spanLiveSetCache[key] -} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/version.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/version.go index 7f97cc31e5..b5497c2816 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/version.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/sdk/version.go @@ -6,5 +6,5 @@ package sdk // import "go.opentelemetry.io/otel/sdk" // Version is the current release version of the OpenTelemetry SDK in use. func Version() string { - return "1.38.0" + return "1.40.0" } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/error_type.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/error_type.go index 666bded4ba..267979c051 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/error_type.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/error_type.go @@ -4,28 +4,53 @@ package semconv // import "go.opentelemetry.io/otel/semconv/v1.37.0" import ( - "fmt" "reflect" "go.opentelemetry.io/otel/attribute" ) // ErrorType returns an [attribute.KeyValue] identifying the error type of err. +// +// If err is nil, the returned attribute has the default value +// [ErrorTypeOther]. +// +// If err's type has the method +// +// ErrorType() string +// +// then the returned attribute has the value of err.ErrorType(). Otherwise, the +// returned attribute has a value derived from the concrete type of err. +// +// The key of the returned attribute is [ErrorTypeKey]. func ErrorType(err error) attribute.KeyValue { if err == nil { return ErrorTypeOther } - t := reflect.TypeOf(err) - var value string - if t.PkgPath() == "" && t.Name() == "" { - // Likely a builtin type. - value = t.String() - } else { - value = fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()) + + return ErrorTypeKey.String(errorType(err)) +} + +func errorType(err error) string { + var s string + if et, ok := err.(interface{ ErrorType() string }); ok { + // Prioritize the ErrorType method if available. + s = et.ErrorType() } + if s == "" { + // Fallback to reflection if the ErrorType method is not supported or + // returns an empty value. - if value == "" { - return ErrorTypeOther + t := reflect.TypeOf(err) + pkg, name := t.PkgPath(), t.Name() + if pkg != "" && name != "" { + s = pkg + "." + name + } else { + // The type has no package path or name (predeclared, not-defined, + // or alias for a not-defined type). + // + // This is not guaranteed to be unique, but is a best effort. + s = t.String() + } } - return ErrorTypeKey.String(value) + return s } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/MIGRATION.md b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/MIGRATION.md new file mode 100644 index 0000000000..fed7013e6f --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/MIGRATION.md @@ -0,0 +1,78 @@ + +# Migration from v1.38.0 to v1.39.0 + +The `go.opentelemetry.io/otel/semconv/v1.39.0` package should be a drop-in replacement for `go.opentelemetry.io/otel/semconv/v1.38.0` with the following exceptions. + +## Removed + +The following declarations have been removed. +Refer to the [OpenTelemetry Semantic Conventions documentation] for deprecation instructions. + +If the type is not listed in the documentation as deprecated, it has been removed in this version due to lack of applicability or use. +If you use any of these non-deprecated declarations in your Go application, please [open an issue] describing your use-case. + +- `LinuxMemorySlabStateKey` +- `LinuxMemorySlabStateReclaimable` +- `LinuxMemorySlabStateUnreclaimable` +- `PeerService` +- `PeerServiceKey` +- `RPCConnectRPCErrorCodeAborted` +- `RPCConnectRPCErrorCodeAlreadyExists` +- `RPCConnectRPCErrorCodeCancelled` +- `RPCConnectRPCErrorCodeDataLoss` +- `RPCConnectRPCErrorCodeDeadlineExceeded` +- `RPCConnectRPCErrorCodeFailedPrecondition` +- `RPCConnectRPCErrorCodeInternal` +- `RPCConnectRPCErrorCodeInvalidArgument` +- `RPCConnectRPCErrorCodeKey` +- `RPCConnectRPCErrorCodeNotFound` +- `RPCConnectRPCErrorCodeOutOfRange` +- `RPCConnectRPCErrorCodePermissionDenied` +- `RPCConnectRPCErrorCodeResourceExhausted` +- `RPCConnectRPCErrorCodeUnauthenticated` +- `RPCConnectRPCErrorCodeUnavailable` +- `RPCConnectRPCErrorCodeUnimplemented` +- `RPCConnectRPCErrorCodeUnknown` +- `RPCConnectRPCRequestMetadata` +- `RPCConnectRPCResponseMetadata` +- `RPCGRPCRequestMetadata` +- `RPCGRPCResponseMetadata` +- `RPCGRPCStatusCodeAborted` +- `RPCGRPCStatusCodeAlreadyExists` +- `RPCGRPCStatusCodeCancelled` +- `RPCGRPCStatusCodeDataLoss` +- `RPCGRPCStatusCodeDeadlineExceeded` +- `RPCGRPCStatusCodeFailedPrecondition` +- `RPCGRPCStatusCodeInternal` +- `RPCGRPCStatusCodeInvalidArgument` +- `RPCGRPCStatusCodeKey` +- `RPCGRPCStatusCodeNotFound` +- `RPCGRPCStatusCodeOk` +- `RPCGRPCStatusCodeOutOfRange` +- `RPCGRPCStatusCodePermissionDenied` +- `RPCGRPCStatusCodeResourceExhausted` +- `RPCGRPCStatusCodeUnauthenticated` +- `RPCGRPCStatusCodeUnavailable` +- `RPCGRPCStatusCodeUnimplemented` +- `RPCGRPCStatusCodeUnknown` +- `RPCJSONRPCErrorCode` +- `RPCJSONRPCErrorCodeKey` +- `RPCJSONRPCErrorMessage` +- `RPCJSONRPCErrorMessageKey` +- `RPCJSONRPCRequestID` +- `RPCJSONRPCRequestIDKey` +- `RPCJSONRPCVersion` +- `RPCJSONRPCVersionKey` +- `RPCService` +- `RPCServiceKey` +- `RPCSystemApacheDubbo` +- `RPCSystemConnectRPC` +- `RPCSystemDotnetWcf` +- `RPCSystemGRPC` +- `RPCSystemJSONRPC` +- `RPCSystemJavaRmi` +- `RPCSystemKey` +- `RPCSystemOncRPC` + +[OpenTelemetry Semantic Conventions documentation]: https://github.com/open-telemetry/semantic-conventions +[open an issue]: https://github.com/open-telemetry/opentelemetry-go/issues/new?template=Blank+issue diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/README.md b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/README.md new file mode 100644 index 0000000000..4b0e6f7f3e --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/README.md @@ -0,0 +1,3 @@ +# Semconv v1.39.0 + +[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/semconv/v1.39.0)](https://pkg.go.dev/go.opentelemetry.io/otel/semconv/v1.39.0) diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/attribute_group.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/attribute_group.go new file mode 100644 index 0000000000..080365fc19 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/attribute_group.go @@ -0,0 +1,16239 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Code generated from semantic convention specification. DO NOT EDIT. + +package semconv // import "go.opentelemetry.io/otel/semconv/v1.39.0" + +import "go.opentelemetry.io/otel/attribute" + +// Namespace: android +const ( + // AndroidAppStateKey is the attribute Key conforming to the "android.app.state" + // semantic conventions. It represents the this attribute represents the state + // of the application. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "created" + // Note: The Android lifecycle states are defined in + // [Activity lifecycle callbacks], and from which the `OS identifiers` are + // derived. + // + // [Activity lifecycle callbacks]: https://developer.android.com/guide/components/activities/activity-lifecycle#lc + AndroidAppStateKey = attribute.Key("android.app.state") + + // AndroidOSAPILevelKey is the attribute Key conforming to the + // "android.os.api_level" semantic conventions. It represents the uniquely + // identifies the framework API revision offered by a version (`os.version`) of + // the android operating system. More information can be found in the + // [Android API levels documentation]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "33", "32" + // + // [Android API levels documentation]: https://developer.android.com/guide/topics/manifest/uses-sdk-element#ApiLevels + AndroidOSAPILevelKey = attribute.Key("android.os.api_level") +) + +// AndroidOSAPILevel returns an attribute KeyValue conforming to the +// "android.os.api_level" semantic conventions. It represents the uniquely +// identifies the framework API revision offered by a version (`os.version`) of +// the android operating system. More information can be found in the +// [Android API levels documentation]. +// +// [Android API levels documentation]: https://developer.android.com/guide/topics/manifest/uses-sdk-element#ApiLevels +func AndroidOSAPILevel(val string) attribute.KeyValue { + return AndroidOSAPILevelKey.String(val) +} + +// Enum values for android.app.state +var ( + // Any time before Activity.onResume() or, if the app has no Activity, + // Context.startService() has been called in the app for the first time. + // + // Stability: development + AndroidAppStateCreated = AndroidAppStateKey.String("created") + // Any time after Activity.onPause() or, if the app has no Activity, + // Context.stopService() has been called when the app was in the foreground + // state. + // + // Stability: development + AndroidAppStateBackground = AndroidAppStateKey.String("background") + // Any time after Activity.onResume() or, if the app has no Activity, + // Context.startService() has been called when the app was in either the created + // or background states. + // + // Stability: development + AndroidAppStateForeground = AndroidAppStateKey.String("foreground") +) + +// Namespace: app +const ( + // AppBuildIDKey is the attribute Key conforming to the "app.build_id" semantic + // conventions. It represents the unique identifier for a particular build or + // compilation of the application. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "6cff0a7e-cefc-4668-96f5-1273d8b334d0", + // "9f2b833506aa6973a92fde9733e6271f", "my-app-1.0.0-code-123" + AppBuildIDKey = attribute.Key("app.build_id") + + // AppInstallationIDKey is the attribute Key conforming to the + // "app.installation.id" semantic conventions. It represents a unique identifier + // representing the installation of an application on a specific device. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2ab2916d-a51f-4ac8-80ee-45ac31a28092" + // Note: Its value SHOULD persist across launches of the same application + // installation, including through application upgrades. + // It SHOULD change if the application is uninstalled or if all applications of + // the vendor are uninstalled. + // Additionally, users might be able to reset this value (e.g. by clearing + // application data). + // If an app is installed multiple times on the same device (e.g. in different + // accounts on Android), each `app.installation.id` SHOULD have a different + // value. + // If multiple OpenTelemetry SDKs are used within the same application, they + // SHOULD use the same value for `app.installation.id`. + // Hardware IDs (e.g. serial number, IMEI, MAC address) MUST NOT be used as the + // `app.installation.id`. + // + // For iOS, this value SHOULD be equal to the [vendor identifier]. + // + // For Android, examples of `app.installation.id` implementations include: + // + // - [Firebase Installation ID]. + // - A globally unique UUID which is persisted across sessions in your + // application. + // - [App set ID]. + // - [`Settings.getString(Settings.Secure.ANDROID_ID)`]. + // + // More information about Android identifier best practices can be found in the + // [Android user data IDs guide]. + // + // [vendor identifier]: https://developer.apple.com/documentation/uikit/uidevice/identifierforvendor + // [Firebase Installation ID]: https://firebase.google.com/docs/projects/manage-installations + // [App set ID]: https://developer.android.com/identity/app-set-id + // [`Settings.getString(Settings.Secure.ANDROID_ID)`]: https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID + // [Android user data IDs guide]: https://developer.android.com/training/articles/user-data-ids + AppInstallationIDKey = attribute.Key("app.installation.id") + + // AppJankFrameCountKey is the attribute Key conforming to the + // "app.jank.frame_count" semantic conventions. It represents a number of frame + // renders that experienced jank. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 9, 42 + // Note: Depending on platform limitations, the value provided MAY be + // approximation. + AppJankFrameCountKey = attribute.Key("app.jank.frame_count") + + // AppJankPeriodKey is the attribute Key conforming to the "app.jank.period" + // semantic conventions. It represents the time period, in seconds, for which + // this jank is being reported. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1.0, 5.0, 10.24 + AppJankPeriodKey = attribute.Key("app.jank.period") + + // AppJankThresholdKey is the attribute Key conforming to the + // "app.jank.threshold" semantic conventions. It represents the minimum + // rendering threshold for this jank, in seconds. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 0.016, 0.7, 1.024 + AppJankThresholdKey = attribute.Key("app.jank.threshold") + + // AppScreenCoordinateXKey is the attribute Key conforming to the + // "app.screen.coordinate.x" semantic conventions. It represents the x + // (horizontal) coordinate of a screen coordinate, in screen pixels. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 0, 131 + AppScreenCoordinateXKey = attribute.Key("app.screen.coordinate.x") + + // AppScreenCoordinateYKey is the attribute Key conforming to the + // "app.screen.coordinate.y" semantic conventions. It represents the y + // (vertical) component of a screen coordinate, in screen pixels. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 12, 99 + AppScreenCoordinateYKey = attribute.Key("app.screen.coordinate.y") + + // AppScreenIDKey is the attribute Key conforming to the "app.screen.id" + // semantic conventions. It represents an identifier that uniquely + // differentiates this screen from other screens in the same application. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "f9bc787d-ff05-48ad-90e1-fca1d46130b3", + // "com.example.app.MainActivity", "com.example.shop.ProductDetailFragment", + // "MyApp.ProfileView", "MyApp.ProfileViewController" + // Note: A screen represents only the part of the device display drawn by the + // app. It typically contains multiple widgets or UI components and is larger in + // scope than individual widgets. Multiple screens can coexist on the same + // display simultaneously (e.g., split view on tablets). + AppScreenIDKey = attribute.Key("app.screen.id") + + // AppScreenNameKey is the attribute Key conforming to the "app.screen.name" + // semantic conventions. It represents the name of an application screen. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "MainActivity", "ProductDetailFragment", "ProfileView", + // "ProfileViewController" + // Note: A screen represents only the part of the device display drawn by the + // app. It typically contains multiple widgets or UI components and is larger in + // scope than individual widgets. Multiple screens can coexist on the same + // display simultaneously (e.g., split view on tablets). + AppScreenNameKey = attribute.Key("app.screen.name") + + // AppWidgetIDKey is the attribute Key conforming to the "app.widget.id" + // semantic conventions. It represents an identifier that uniquely + // differentiates this widget from other widgets in the same application. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "f9bc787d-ff05-48ad-90e1-fca1d46130b3", "submit_order_1829" + // Note: A widget is an application component, typically an on-screen visual GUI + // element. + AppWidgetIDKey = attribute.Key("app.widget.id") + + // AppWidgetNameKey is the attribute Key conforming to the "app.widget.name" + // semantic conventions. It represents the name of an application widget. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "submit", "attack", "Clear Cart" + // Note: A widget is an application component, typically an on-screen visual GUI + // element. + AppWidgetNameKey = attribute.Key("app.widget.name") +) + +// AppBuildID returns an attribute KeyValue conforming to the "app.build_id" +// semantic conventions. It represents the unique identifier for a particular +// build or compilation of the application. +func AppBuildID(val string) attribute.KeyValue { + return AppBuildIDKey.String(val) +} + +// AppInstallationID returns an attribute KeyValue conforming to the +// "app.installation.id" semantic conventions. It represents a unique identifier +// representing the installation of an application on a specific device. +func AppInstallationID(val string) attribute.KeyValue { + return AppInstallationIDKey.String(val) +} + +// AppJankFrameCount returns an attribute KeyValue conforming to the +// "app.jank.frame_count" semantic conventions. It represents a number of frame +// renders that experienced jank. +func AppJankFrameCount(val int) attribute.KeyValue { + return AppJankFrameCountKey.Int(val) +} + +// AppJankPeriod returns an attribute KeyValue conforming to the +// "app.jank.period" semantic conventions. It represents the time period, in +// seconds, for which this jank is being reported. +func AppJankPeriod(val float64) attribute.KeyValue { + return AppJankPeriodKey.Float64(val) +} + +// AppJankThreshold returns an attribute KeyValue conforming to the +// "app.jank.threshold" semantic conventions. It represents the minimum rendering +// threshold for this jank, in seconds. +func AppJankThreshold(val float64) attribute.KeyValue { + return AppJankThresholdKey.Float64(val) +} + +// AppScreenCoordinateX returns an attribute KeyValue conforming to the +// "app.screen.coordinate.x" semantic conventions. It represents the x +// (horizontal) coordinate of a screen coordinate, in screen pixels. +func AppScreenCoordinateX(val int) attribute.KeyValue { + return AppScreenCoordinateXKey.Int(val) +} + +// AppScreenCoordinateY returns an attribute KeyValue conforming to the +// "app.screen.coordinate.y" semantic conventions. It represents the y (vertical) +// component of a screen coordinate, in screen pixels. +func AppScreenCoordinateY(val int) attribute.KeyValue { + return AppScreenCoordinateYKey.Int(val) +} + +// AppScreenID returns an attribute KeyValue conforming to the "app.screen.id" +// semantic conventions. It represents an identifier that uniquely differentiates +// this screen from other screens in the same application. +func AppScreenID(val string) attribute.KeyValue { + return AppScreenIDKey.String(val) +} + +// AppScreenName returns an attribute KeyValue conforming to the +// "app.screen.name" semantic conventions. It represents the name of an +// application screen. +func AppScreenName(val string) attribute.KeyValue { + return AppScreenNameKey.String(val) +} + +// AppWidgetID returns an attribute KeyValue conforming to the "app.widget.id" +// semantic conventions. It represents an identifier that uniquely differentiates +// this widget from other widgets in the same application. +func AppWidgetID(val string) attribute.KeyValue { + return AppWidgetIDKey.String(val) +} + +// AppWidgetName returns an attribute KeyValue conforming to the +// "app.widget.name" semantic conventions. It represents the name of an +// application widget. +func AppWidgetName(val string) attribute.KeyValue { + return AppWidgetNameKey.String(val) +} + +// Namespace: artifact +const ( + // ArtifactAttestationFilenameKey is the attribute Key conforming to the + // "artifact.attestation.filename" semantic conventions. It represents the + // provenance filename of the built attestation which directly relates to the + // build artifact filename. This filename SHOULD accompany the artifact at + // publish time. See the [SLSA Relationship] specification for more information. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "golang-binary-amd64-v0.1.0.attestation", + // "docker-image-amd64-v0.1.0.intoto.json1", "release-1.tar.gz.attestation", + // "file-name-package.tar.gz.intoto.json1" + // + // [SLSA Relationship]: https://slsa.dev/spec/v1.0/distributing-provenance#relationship-between-artifacts-and-attestations + ArtifactAttestationFilenameKey = attribute.Key("artifact.attestation.filename") + + // ArtifactAttestationHashKey is the attribute Key conforming to the + // "artifact.attestation.hash" semantic conventions. It represents the full + // [hash value (see glossary)], of the built attestation. Some envelopes in the + // [software attestation space] also refer to this as the **digest**. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "1b31dfcd5b7f9267bf2ff47651df1cfb9147b9e4df1f335accf65b4cda498408" + // + // [hash value (see glossary)]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf + // [software attestation space]: https://github.com/in-toto/attestation/tree/main/spec + ArtifactAttestationHashKey = attribute.Key("artifact.attestation.hash") + + // ArtifactAttestationIDKey is the attribute Key conforming to the + // "artifact.attestation.id" semantic conventions. It represents the id of the + // build [software attestation]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "123" + // + // [software attestation]: https://slsa.dev/attestation-model + ArtifactAttestationIDKey = attribute.Key("artifact.attestation.id") + + // ArtifactFilenameKey is the attribute Key conforming to the + // "artifact.filename" semantic conventions. It represents the human readable + // file name of the artifact, typically generated during build and release + // processes. Often includes the package name and version in the file name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "golang-binary-amd64-v0.1.0", "docker-image-amd64-v0.1.0", + // "release-1.tar.gz", "file-name-package.tar.gz" + // Note: This file name can also act as the [Package Name] + // in cases where the package ecosystem maps accordingly. + // Additionally, the artifact [can be published] + // for others, but that is not a guarantee. + // + // [Package Name]: https://slsa.dev/spec/v1.0/terminology#package-model + // [can be published]: https://slsa.dev/spec/v1.0/terminology#software-supply-chain + ArtifactFilenameKey = attribute.Key("artifact.filename") + + // ArtifactHashKey is the attribute Key conforming to the "artifact.hash" + // semantic conventions. It represents the full [hash value (see glossary)], + // often found in checksum.txt on a release of the artifact and used to verify + // package integrity. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "9ff4c52759e2c4ac70b7d517bc7fcdc1cda631ca0045271ddd1b192544f8a3e9" + // Note: The specific algorithm used to create the cryptographic hash value is + // not defined. In situations where an artifact has multiple + // cryptographic hashes, it is up to the implementer to choose which + // hash value to set here; this should be the most secure hash algorithm + // that is suitable for the situation and consistent with the + // corresponding attestation. The implementer can then provide the other + // hash values through an additional set of attribute extensions as they + // deem necessary. + // + // [hash value (see glossary)]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf + ArtifactHashKey = attribute.Key("artifact.hash") + + // ArtifactPurlKey is the attribute Key conforming to the "artifact.purl" + // semantic conventions. It represents the [Package URL] of the + // [package artifact] provides a standard way to identify and locate the + // packaged artifact. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "pkg:github/package-url/purl-spec@1209109710924", + // "pkg:npm/foo@12.12.3" + // + // [Package URL]: https://github.com/package-url/purl-spec + // [package artifact]: https://slsa.dev/spec/v1.0/terminology#package-model + ArtifactPurlKey = attribute.Key("artifact.purl") + + // ArtifactVersionKey is the attribute Key conforming to the "artifact.version" + // semantic conventions. It represents the version of the artifact. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "v0.1.0", "1.2.1", "122691-build" + ArtifactVersionKey = attribute.Key("artifact.version") +) + +// ArtifactAttestationFilename returns an attribute KeyValue conforming to the +// "artifact.attestation.filename" semantic conventions. It represents the +// provenance filename of the built attestation which directly relates to the +// build artifact filename. This filename SHOULD accompany the artifact at +// publish time. See the [SLSA Relationship] specification for more information. +// +// [SLSA Relationship]: https://slsa.dev/spec/v1.0/distributing-provenance#relationship-between-artifacts-and-attestations +func ArtifactAttestationFilename(val string) attribute.KeyValue { + return ArtifactAttestationFilenameKey.String(val) +} + +// ArtifactAttestationHash returns an attribute KeyValue conforming to the +// "artifact.attestation.hash" semantic conventions. It represents the full +// [hash value (see glossary)], of the built attestation. Some envelopes in the +// [software attestation space] also refer to this as the **digest**. +// +// [hash value (see glossary)]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf +// [software attestation space]: https://github.com/in-toto/attestation/tree/main/spec +func ArtifactAttestationHash(val string) attribute.KeyValue { + return ArtifactAttestationHashKey.String(val) +} + +// ArtifactAttestationID returns an attribute KeyValue conforming to the +// "artifact.attestation.id" semantic conventions. It represents the id of the +// build [software attestation]. +// +// [software attestation]: https://slsa.dev/attestation-model +func ArtifactAttestationID(val string) attribute.KeyValue { + return ArtifactAttestationIDKey.String(val) +} + +// ArtifactFilename returns an attribute KeyValue conforming to the +// "artifact.filename" semantic conventions. It represents the human readable +// file name of the artifact, typically generated during build and release +// processes. Often includes the package name and version in the file name. +func ArtifactFilename(val string) attribute.KeyValue { + return ArtifactFilenameKey.String(val) +} + +// ArtifactHash returns an attribute KeyValue conforming to the "artifact.hash" +// semantic conventions. It represents the full [hash value (see glossary)], +// often found in checksum.txt on a release of the artifact and used to verify +// package integrity. +// +// [hash value (see glossary)]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf +func ArtifactHash(val string) attribute.KeyValue { + return ArtifactHashKey.String(val) +} + +// ArtifactPurl returns an attribute KeyValue conforming to the "artifact.purl" +// semantic conventions. It represents the [Package URL] of the +// [package artifact] provides a standard way to identify and locate the packaged +// artifact. +// +// [Package URL]: https://github.com/package-url/purl-spec +// [package artifact]: https://slsa.dev/spec/v1.0/terminology#package-model +func ArtifactPurl(val string) attribute.KeyValue { + return ArtifactPurlKey.String(val) +} + +// ArtifactVersion returns an attribute KeyValue conforming to the +// "artifact.version" semantic conventions. It represents the version of the +// artifact. +func ArtifactVersion(val string) attribute.KeyValue { + return ArtifactVersionKey.String(val) +} + +// Namespace: aws +const ( + // AWSBedrockGuardrailIDKey is the attribute Key conforming to the + // "aws.bedrock.guardrail.id" semantic conventions. It represents the unique + // identifier of the AWS Bedrock Guardrail. A [guardrail] helps safeguard and + // prevent unwanted behavior from model responses or user messages. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "sgi5gkybzqak" + // + // [guardrail]: https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html + AWSBedrockGuardrailIDKey = attribute.Key("aws.bedrock.guardrail.id") + + // AWSBedrockKnowledgeBaseIDKey is the attribute Key conforming to the + // "aws.bedrock.knowledge_base.id" semantic conventions. It represents the + // unique identifier of the AWS Bedrock Knowledge base. A [knowledge base] is a + // bank of information that can be queried by models to generate more relevant + // responses and augment prompts. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "XFWUPB9PAW" + // + // [knowledge base]: https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base.html + AWSBedrockKnowledgeBaseIDKey = attribute.Key("aws.bedrock.knowledge_base.id") + + // AWSDynamoDBAttributeDefinitionsKey is the attribute Key conforming to the + // "aws.dynamodb.attribute_definitions" semantic conventions. It represents the + // JSON-serialized value of each item in the `AttributeDefinitions` request + // field. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "{ "AttributeName": "string", "AttributeType": "string" }" + AWSDynamoDBAttributeDefinitionsKey = attribute.Key("aws.dynamodb.attribute_definitions") + + // AWSDynamoDBAttributesToGetKey is the attribute Key conforming to the + // "aws.dynamodb.attributes_to_get" semantic conventions. It represents the + // value of the `AttributesToGet` request parameter. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "lives", "id" + AWSDynamoDBAttributesToGetKey = attribute.Key("aws.dynamodb.attributes_to_get") + + // AWSDynamoDBConsistentReadKey is the attribute Key conforming to the + // "aws.dynamodb.consistent_read" semantic conventions. It represents the value + // of the `ConsistentRead` request parameter. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + AWSDynamoDBConsistentReadKey = attribute.Key("aws.dynamodb.consistent_read") + + // AWSDynamoDBConsumedCapacityKey is the attribute Key conforming to the + // "aws.dynamodb.consumed_capacity" semantic conventions. It represents the + // JSON-serialized value of each item in the `ConsumedCapacity` response field. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "{ "CapacityUnits": number, "GlobalSecondaryIndexes": { "string" : + // { "CapacityUnits": number, "ReadCapacityUnits": number, "WriteCapacityUnits": + // number } }, "LocalSecondaryIndexes": { "string" : { "CapacityUnits": number, + // "ReadCapacityUnits": number, "WriteCapacityUnits": number } }, + // "ReadCapacityUnits": number, "Table": { "CapacityUnits": number, + // "ReadCapacityUnits": number, "WriteCapacityUnits": number }, "TableName": + // "string", "WriteCapacityUnits": number }" + AWSDynamoDBConsumedCapacityKey = attribute.Key("aws.dynamodb.consumed_capacity") + + // AWSDynamoDBCountKey is the attribute Key conforming to the + // "aws.dynamodb.count" semantic conventions. It represents the value of the + // `Count` response parameter. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 10 + AWSDynamoDBCountKey = attribute.Key("aws.dynamodb.count") + + // AWSDynamoDBExclusiveStartTableKey is the attribute Key conforming to the + // "aws.dynamodb.exclusive_start_table" semantic conventions. It represents the + // value of the `ExclusiveStartTableName` request parameter. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Users", "CatsTable" + AWSDynamoDBExclusiveStartTableKey = attribute.Key("aws.dynamodb.exclusive_start_table") + + // AWSDynamoDBGlobalSecondaryIndexUpdatesKey is the attribute Key conforming to + // the "aws.dynamodb.global_secondary_index_updates" semantic conventions. It + // represents the JSON-serialized value of each item in the + // `GlobalSecondaryIndexUpdates` request field. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "{ "Create": { "IndexName": "string", "KeySchema": [ { + // "AttributeName": "string", "KeyType": "string" } ], "Projection": { + // "NonKeyAttributes": [ "string" ], "ProjectionType": "string" }, + // "ProvisionedThroughput": { "ReadCapacityUnits": number, "WriteCapacityUnits": + // number } }" + AWSDynamoDBGlobalSecondaryIndexUpdatesKey = attribute.Key("aws.dynamodb.global_secondary_index_updates") + + // AWSDynamoDBGlobalSecondaryIndexesKey is the attribute Key conforming to the + // "aws.dynamodb.global_secondary_indexes" semantic conventions. It represents + // the JSON-serialized value of each item of the `GlobalSecondaryIndexes` + // request field. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "{ "IndexName": "string", "KeySchema": [ { "AttributeName": + // "string", "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ + // "string" ], "ProjectionType": "string" }, "ProvisionedThroughput": { + // "ReadCapacityUnits": number, "WriteCapacityUnits": number } }" + AWSDynamoDBGlobalSecondaryIndexesKey = attribute.Key("aws.dynamodb.global_secondary_indexes") + + // AWSDynamoDBIndexNameKey is the attribute Key conforming to the + // "aws.dynamodb.index_name" semantic conventions. It represents the value of + // the `IndexName` request parameter. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "name_to_group" + AWSDynamoDBIndexNameKey = attribute.Key("aws.dynamodb.index_name") + + // AWSDynamoDBItemCollectionMetricsKey is the attribute Key conforming to the + // "aws.dynamodb.item_collection_metrics" semantic conventions. It represents + // the JSON-serialized value of the `ItemCollectionMetrics` response field. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "{ "string" : [ { "ItemCollectionKey": { "string" : { "B": blob, + // "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : + // "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": + // "string", "SS": [ "string" ] } }, "SizeEstimateRangeGB": [ number ] } ] }" + AWSDynamoDBItemCollectionMetricsKey = attribute.Key("aws.dynamodb.item_collection_metrics") + + // AWSDynamoDBLimitKey is the attribute Key conforming to the + // "aws.dynamodb.limit" semantic conventions. It represents the value of the + // `Limit` request parameter. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 10 + AWSDynamoDBLimitKey = attribute.Key("aws.dynamodb.limit") + + // AWSDynamoDBLocalSecondaryIndexesKey is the attribute Key conforming to the + // "aws.dynamodb.local_secondary_indexes" semantic conventions. It represents + // the JSON-serialized value of each item of the `LocalSecondaryIndexes` request + // field. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "{ "IndexArn": "string", "IndexName": "string", "IndexSizeBytes": + // number, "ItemCount": number, "KeySchema": [ { "AttributeName": "string", + // "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ "string" ], + // "ProjectionType": "string" } }" + AWSDynamoDBLocalSecondaryIndexesKey = attribute.Key("aws.dynamodb.local_secondary_indexes") + + // AWSDynamoDBProjectionKey is the attribute Key conforming to the + // "aws.dynamodb.projection" semantic conventions. It represents the value of + // the `ProjectionExpression` request parameter. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Title", "Title, Price, Color", "Title, Description, RelatedItems, + // ProductReviews" + AWSDynamoDBProjectionKey = attribute.Key("aws.dynamodb.projection") + + // AWSDynamoDBProvisionedReadCapacityKey is the attribute Key conforming to the + // "aws.dynamodb.provisioned_read_capacity" semantic conventions. It represents + // the value of the `ProvisionedThroughput.ReadCapacityUnits` request parameter. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1.0, 2.0 + AWSDynamoDBProvisionedReadCapacityKey = attribute.Key("aws.dynamodb.provisioned_read_capacity") + + // AWSDynamoDBProvisionedWriteCapacityKey is the attribute Key conforming to the + // "aws.dynamodb.provisioned_write_capacity" semantic conventions. It represents + // the value of the `ProvisionedThroughput.WriteCapacityUnits` request + // parameter. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1.0, 2.0 + AWSDynamoDBProvisionedWriteCapacityKey = attribute.Key("aws.dynamodb.provisioned_write_capacity") + + // AWSDynamoDBScanForwardKey is the attribute Key conforming to the + // "aws.dynamodb.scan_forward" semantic conventions. It represents the value of + // the `ScanIndexForward` request parameter. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + AWSDynamoDBScanForwardKey = attribute.Key("aws.dynamodb.scan_forward") + + // AWSDynamoDBScannedCountKey is the attribute Key conforming to the + // "aws.dynamodb.scanned_count" semantic conventions. It represents the value of + // the `ScannedCount` response parameter. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 50 + AWSDynamoDBScannedCountKey = attribute.Key("aws.dynamodb.scanned_count") + + // AWSDynamoDBSegmentKey is the attribute Key conforming to the + // "aws.dynamodb.segment" semantic conventions. It represents the value of the + // `Segment` request parameter. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 10 + AWSDynamoDBSegmentKey = attribute.Key("aws.dynamodb.segment") + + // AWSDynamoDBSelectKey is the attribute Key conforming to the + // "aws.dynamodb.select" semantic conventions. It represents the value of the + // `Select` request parameter. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "ALL_ATTRIBUTES", "COUNT" + AWSDynamoDBSelectKey = attribute.Key("aws.dynamodb.select") + + // AWSDynamoDBTableCountKey is the attribute Key conforming to the + // "aws.dynamodb.table_count" semantic conventions. It represents the number of + // items in the `TableNames` response parameter. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 20 + AWSDynamoDBTableCountKey = attribute.Key("aws.dynamodb.table_count") + + // AWSDynamoDBTableNamesKey is the attribute Key conforming to the + // "aws.dynamodb.table_names" semantic conventions. It represents the keys in + // the `RequestItems` object field. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Users", "Cats" + AWSDynamoDBTableNamesKey = attribute.Key("aws.dynamodb.table_names") + + // AWSDynamoDBTotalSegmentsKey is the attribute Key conforming to the + // "aws.dynamodb.total_segments" semantic conventions. It represents the value + // of the `TotalSegments` request parameter. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 100 + AWSDynamoDBTotalSegmentsKey = attribute.Key("aws.dynamodb.total_segments") + + // AWSECSClusterARNKey is the attribute Key conforming to the + // "aws.ecs.cluster.arn" semantic conventions. It represents the ARN of an + // [ECS cluster]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster" + // + // [ECS cluster]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html + AWSECSClusterARNKey = attribute.Key("aws.ecs.cluster.arn") + + // AWSECSContainerARNKey is the attribute Key conforming to the + // "aws.ecs.container.arn" semantic conventions. It represents the Amazon + // Resource Name (ARN) of an [ECS container instance]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "arn:aws:ecs:us-west-1:123456789123:container/32624152-9086-4f0e-acae-1a75b14fe4d9" + // + // [ECS container instance]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_instances.html + AWSECSContainerARNKey = attribute.Key("aws.ecs.container.arn") + + // AWSECSLaunchtypeKey is the attribute Key conforming to the + // "aws.ecs.launchtype" semantic conventions. It represents the [launch type] + // for an ECS task. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // + // [launch type]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/launch_types.html + AWSECSLaunchtypeKey = attribute.Key("aws.ecs.launchtype") + + // AWSECSTaskARNKey is the attribute Key conforming to the "aws.ecs.task.arn" + // semantic conventions. It represents the ARN of a running [ECS task]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "arn:aws:ecs:us-west-1:123456789123:task/10838bed-421f-43ef-870a-f43feacbbb5b", + // "arn:aws:ecs:us-west-1:123456789123:task/my-cluster/task-id/23ebb8ac-c18f-46c6-8bbe-d55d0e37cfbd" + // + // [ECS task]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-account-settings.html#ecs-resource-ids + AWSECSTaskARNKey = attribute.Key("aws.ecs.task.arn") + + // AWSECSTaskFamilyKey is the attribute Key conforming to the + // "aws.ecs.task.family" semantic conventions. It represents the family name of + // the [ECS task definition] used to create the ECS task. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "opentelemetry-family" + // + // [ECS task definition]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html + AWSECSTaskFamilyKey = attribute.Key("aws.ecs.task.family") + + // AWSECSTaskIDKey is the attribute Key conforming to the "aws.ecs.task.id" + // semantic conventions. It represents the ID of a running ECS task. The ID MUST + // be extracted from `task.arn`. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "10838bed-421f-43ef-870a-f43feacbbb5b", + // "23ebb8ac-c18f-46c6-8bbe-d55d0e37cfbd" + AWSECSTaskIDKey = attribute.Key("aws.ecs.task.id") + + // AWSECSTaskRevisionKey is the attribute Key conforming to the + // "aws.ecs.task.revision" semantic conventions. It represents the revision for + // the task definition used to create the ECS task. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "8", "26" + AWSECSTaskRevisionKey = attribute.Key("aws.ecs.task.revision") + + // AWSEKSClusterARNKey is the attribute Key conforming to the + // "aws.eks.cluster.arn" semantic conventions. It represents the ARN of an EKS + // cluster. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster" + AWSEKSClusterARNKey = attribute.Key("aws.eks.cluster.arn") + + // AWSExtendedRequestIDKey is the attribute Key conforming to the + // "aws.extended_request_id" semantic conventions. It represents the AWS + // extended request ID as returned in the response header `x-amz-id-2`. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "wzHcyEWfmOGDIE5QOhTAqFDoDWP3y8IUvpNINCwL9N4TEHbUw0/gZJ+VZTmCNCWR7fezEN3eCiQ=" + AWSExtendedRequestIDKey = attribute.Key("aws.extended_request_id") + + // AWSKinesisStreamNameKey is the attribute Key conforming to the + // "aws.kinesis.stream_name" semantic conventions. It represents the name of the + // AWS Kinesis [stream] the request refers to. Corresponds to the + // `--stream-name` parameter of the Kinesis [describe-stream] operation. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "some-stream-name" + // + // [stream]: https://docs.aws.amazon.com/streams/latest/dev/introduction.html + // [describe-stream]: https://docs.aws.amazon.com/cli/latest/reference/kinesis/describe-stream.html + AWSKinesisStreamNameKey = attribute.Key("aws.kinesis.stream_name") + + // AWSLambdaInvokedARNKey is the attribute Key conforming to the + // "aws.lambda.invoked_arn" semantic conventions. It represents the full invoked + // ARN as provided on the `Context` passed to the function ( + // `Lambda-Runtime-Invoked-Function-Arn` header on the + // `/runtime/invocation/next` applicable). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "arn:aws:lambda:us-east-1:123456:function:myfunction:myalias" + // Note: This may be different from `cloud.resource_id` if an alias is involved. + AWSLambdaInvokedARNKey = attribute.Key("aws.lambda.invoked_arn") + + // AWSLambdaResourceMappingIDKey is the attribute Key conforming to the + // "aws.lambda.resource_mapping.id" semantic conventions. It represents the UUID + // of the [AWS Lambda EvenSource Mapping]. An event source is mapped to a lambda + // function. It's contents are read by Lambda and used to trigger a function. + // This isn't available in the lambda execution context or the lambda runtime + // environtment. This is going to be populated by the AWS SDK for each language + // when that UUID is present. Some of these operations are + // Create/Delete/Get/List/Update EventSourceMapping. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "587ad24b-03b9-4413-8202-bbd56b36e5b7" + // + // [AWS Lambda EvenSource Mapping]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html + AWSLambdaResourceMappingIDKey = attribute.Key("aws.lambda.resource_mapping.id") + + // AWSLogGroupARNsKey is the attribute Key conforming to the + // "aws.log.group.arns" semantic conventions. It represents the Amazon Resource + // Name(s) (ARN) of the AWS log group(s). + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:*" + // Note: See the [log group ARN format documentation]. + // + // [log group ARN format documentation]: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format + AWSLogGroupARNsKey = attribute.Key("aws.log.group.arns") + + // AWSLogGroupNamesKey is the attribute Key conforming to the + // "aws.log.group.names" semantic conventions. It represents the name(s) of the + // AWS log group(s) an application is writing to. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/aws/lambda/my-function", "opentelemetry-service" + // Note: Multiple log groups must be supported for cases like multi-container + // applications, where a single application has sidecar containers, and each + // write to their own log group. + AWSLogGroupNamesKey = attribute.Key("aws.log.group.names") + + // AWSLogStreamARNsKey is the attribute Key conforming to the + // "aws.log.stream.arns" semantic conventions. It represents the ARN(s) of the + // AWS log stream(s). + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:log-stream:logs/main/10838bed-421f-43ef-870a-f43feacbbb5b" + // Note: See the [log stream ARN format documentation]. One log group can + // contain several log streams, so these ARNs necessarily identify both a log + // group and a log stream. + // + // [log stream ARN format documentation]: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format + AWSLogStreamARNsKey = attribute.Key("aws.log.stream.arns") + + // AWSLogStreamNamesKey is the attribute Key conforming to the + // "aws.log.stream.names" semantic conventions. It represents the name(s) of the + // AWS log stream(s) an application is writing to. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "logs/main/10838bed-421f-43ef-870a-f43feacbbb5b" + AWSLogStreamNamesKey = attribute.Key("aws.log.stream.names") + + // AWSRequestIDKey is the attribute Key conforming to the "aws.request_id" + // semantic conventions. It represents the AWS request ID as returned in the + // response headers `x-amzn-requestid`, `x-amzn-request-id` or + // `x-amz-request-id`. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "79b9da39-b7ae-508a-a6bc-864b2829c622", "C9ER4AJX75574TDJ" + AWSRequestIDKey = attribute.Key("aws.request_id") + + // AWSS3BucketKey is the attribute Key conforming to the "aws.s3.bucket" + // semantic conventions. It represents the S3 bucket name the request refers to. + // Corresponds to the `--bucket` parameter of the [S3 API] operations. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "some-bucket-name" + // Note: The `bucket` attribute is applicable to all S3 operations that + // reference a bucket, i.e. that require the bucket name as a mandatory + // parameter. + // This applies to almost all S3 operations except `list-buckets`. + // + // [S3 API]: https://docs.aws.amazon.com/cli/latest/reference/s3api/index.html + AWSS3BucketKey = attribute.Key("aws.s3.bucket") + + // AWSS3CopySourceKey is the attribute Key conforming to the + // "aws.s3.copy_source" semantic conventions. It represents the source object + // (in the form `bucket`/`key`) for the copy operation. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "someFile.yml" + // Note: The `copy_source` attribute applies to S3 copy operations and + // corresponds to the `--copy-source` parameter + // of the [copy-object operation within the S3 API]. + // This applies in particular to the following operations: + // + // - [copy-object] + // - [upload-part-copy] + // + // + // [copy-object operation within the S3 API]: https://docs.aws.amazon.com/cli/latest/reference/s3api/copy-object.html + // [copy-object]: https://docs.aws.amazon.com/cli/latest/reference/s3api/copy-object.html + // [upload-part-copy]: https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part-copy.html + AWSS3CopySourceKey = attribute.Key("aws.s3.copy_source") + + // AWSS3DeleteKey is the attribute Key conforming to the "aws.s3.delete" + // semantic conventions. It represents the delete request container that + // specifies the objects to be deleted. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "Objects=[{Key=string,VersionId=string},{Key=string,VersionId=string}],Quiet=boolean" + // Note: The `delete` attribute is only applicable to the [delete-object] + // operation. + // The `delete` attribute corresponds to the `--delete` parameter of the + // [delete-objects operation within the S3 API]. + // + // [delete-object]: https://docs.aws.amazon.com/cli/latest/reference/s3api/delete-object.html + // [delete-objects operation within the S3 API]: https://docs.aws.amazon.com/cli/latest/reference/s3api/delete-objects.html + AWSS3DeleteKey = attribute.Key("aws.s3.delete") + + // AWSS3KeyKey is the attribute Key conforming to the "aws.s3.key" semantic + // conventions. It represents the S3 object key the request refers to. + // Corresponds to the `--key` parameter of the [S3 API] operations. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "someFile.yml" + // Note: The `key` attribute is applicable to all object-related S3 operations, + // i.e. that require the object key as a mandatory parameter. + // This applies in particular to the following operations: + // + // - [copy-object] + // - [delete-object] + // - [get-object] + // - [head-object] + // - [put-object] + // - [restore-object] + // - [select-object-content] + // - [abort-multipart-upload] + // - [complete-multipart-upload] + // - [create-multipart-upload] + // - [list-parts] + // - [upload-part] + // - [upload-part-copy] + // + // + // [S3 API]: https://docs.aws.amazon.com/cli/latest/reference/s3api/index.html + // [copy-object]: https://docs.aws.amazon.com/cli/latest/reference/s3api/copy-object.html + // [delete-object]: https://docs.aws.amazon.com/cli/latest/reference/s3api/delete-object.html + // [get-object]: https://docs.aws.amazon.com/cli/latest/reference/s3api/get-object.html + // [head-object]: https://docs.aws.amazon.com/cli/latest/reference/s3api/head-object.html + // [put-object]: https://docs.aws.amazon.com/cli/latest/reference/s3api/put-object.html + // [restore-object]: https://docs.aws.amazon.com/cli/latest/reference/s3api/restore-object.html + // [select-object-content]: https://docs.aws.amazon.com/cli/latest/reference/s3api/select-object-content.html + // [abort-multipart-upload]: https://docs.aws.amazon.com/cli/latest/reference/s3api/abort-multipart-upload.html + // [complete-multipart-upload]: https://docs.aws.amazon.com/cli/latest/reference/s3api/complete-multipart-upload.html + // [create-multipart-upload]: https://docs.aws.amazon.com/cli/latest/reference/s3api/create-multipart-upload.html + // [list-parts]: https://docs.aws.amazon.com/cli/latest/reference/s3api/list-parts.html + // [upload-part]: https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part.html + // [upload-part-copy]: https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part-copy.html + AWSS3KeyKey = attribute.Key("aws.s3.key") + + // AWSS3PartNumberKey is the attribute Key conforming to the + // "aws.s3.part_number" semantic conventions. It represents the part number of + // the part being uploaded in a multipart-upload operation. This is a positive + // integer between 1 and 10,000. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 3456 + // Note: The `part_number` attribute is only applicable to the [upload-part] + // and [upload-part-copy] operations. + // The `part_number` attribute corresponds to the `--part-number` parameter of + // the + // [upload-part operation within the S3 API]. + // + // [upload-part]: https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part.html + // [upload-part-copy]: https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part-copy.html + // [upload-part operation within the S3 API]: https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part.html + AWSS3PartNumberKey = attribute.Key("aws.s3.part_number") + + // AWSS3UploadIDKey is the attribute Key conforming to the "aws.s3.upload_id" + // semantic conventions. It represents the upload ID that identifies the + // multipart upload. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "dfRtDYWFbkRONycy.Yxwh66Yjlx.cph0gtNBtJ" + // Note: The `upload_id` attribute applies to S3 multipart-upload operations and + // corresponds to the `--upload-id` parameter + // of the [S3 API] multipart operations. + // This applies in particular to the following operations: + // + // - [abort-multipart-upload] + // - [complete-multipart-upload] + // - [list-parts] + // - [upload-part] + // - [upload-part-copy] + // + // + // [S3 API]: https://docs.aws.amazon.com/cli/latest/reference/s3api/index.html + // [abort-multipart-upload]: https://docs.aws.amazon.com/cli/latest/reference/s3api/abort-multipart-upload.html + // [complete-multipart-upload]: https://docs.aws.amazon.com/cli/latest/reference/s3api/complete-multipart-upload.html + // [list-parts]: https://docs.aws.amazon.com/cli/latest/reference/s3api/list-parts.html + // [upload-part]: https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part.html + // [upload-part-copy]: https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part-copy.html + AWSS3UploadIDKey = attribute.Key("aws.s3.upload_id") + + // AWSSecretsmanagerSecretARNKey is the attribute Key conforming to the + // "aws.secretsmanager.secret.arn" semantic conventions. It represents the ARN + // of the Secret stored in the Secrets Mangger. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "arn:aws:secretsmanager:us-east-1:123456789012:secret:SecretName-6RandomCharacters" + AWSSecretsmanagerSecretARNKey = attribute.Key("aws.secretsmanager.secret.arn") + + // AWSSNSTopicARNKey is the attribute Key conforming to the "aws.sns.topic.arn" + // semantic conventions. It represents the ARN of the AWS SNS Topic. An Amazon + // SNS [topic] is a logical access point that acts as a communication channel. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "arn:aws:sns:us-east-1:123456789012:mystack-mytopic-NZJ5JSMVGFIE" + // + // [topic]: https://docs.aws.amazon.com/sns/latest/dg/sns-create-topic.html + AWSSNSTopicARNKey = attribute.Key("aws.sns.topic.arn") + + // AWSSQSQueueURLKey is the attribute Key conforming to the "aws.sqs.queue.url" + // semantic conventions. It represents the URL of the AWS SQS Queue. It's a + // unique identifier for a queue in Amazon Simple Queue Service (SQS) and is + // used to access the queue and perform actions on it. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue" + AWSSQSQueueURLKey = attribute.Key("aws.sqs.queue.url") + + // AWSStepFunctionsActivityARNKey is the attribute Key conforming to the + // "aws.step_functions.activity.arn" semantic conventions. It represents the ARN + // of the AWS Step Functions Activity. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "arn:aws:states:us-east-1:123456789012:activity:get-greeting" + AWSStepFunctionsActivityARNKey = attribute.Key("aws.step_functions.activity.arn") + + // AWSStepFunctionsStateMachineARNKey is the attribute Key conforming to the + // "aws.step_functions.state_machine.arn" semantic conventions. It represents + // the ARN of the AWS Step Functions State Machine. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "arn:aws:states:us-east-1:123456789012:stateMachine:myStateMachine:1" + AWSStepFunctionsStateMachineARNKey = attribute.Key("aws.step_functions.state_machine.arn") +) + +// AWSBedrockGuardrailID returns an attribute KeyValue conforming to the +// "aws.bedrock.guardrail.id" semantic conventions. It represents the unique +// identifier of the AWS Bedrock Guardrail. A [guardrail] helps safeguard and +// prevent unwanted behavior from model responses or user messages. +// +// [guardrail]: https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html +func AWSBedrockGuardrailID(val string) attribute.KeyValue { + return AWSBedrockGuardrailIDKey.String(val) +} + +// AWSBedrockKnowledgeBaseID returns an attribute KeyValue conforming to the +// "aws.bedrock.knowledge_base.id" semantic conventions. It represents the unique +// identifier of the AWS Bedrock Knowledge base. A [knowledge base] is a bank of +// information that can be queried by models to generate more relevant responses +// and augment prompts. +// +// [knowledge base]: https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base.html +func AWSBedrockKnowledgeBaseID(val string) attribute.KeyValue { + return AWSBedrockKnowledgeBaseIDKey.String(val) +} + +// AWSDynamoDBAttributeDefinitions returns an attribute KeyValue conforming to +// the "aws.dynamodb.attribute_definitions" semantic conventions. It represents +// the JSON-serialized value of each item in the `AttributeDefinitions` request +// field. +func AWSDynamoDBAttributeDefinitions(val ...string) attribute.KeyValue { + return AWSDynamoDBAttributeDefinitionsKey.StringSlice(val) +} + +// AWSDynamoDBAttributesToGet returns an attribute KeyValue conforming to the +// "aws.dynamodb.attributes_to_get" semantic conventions. It represents the value +// of the `AttributesToGet` request parameter. +func AWSDynamoDBAttributesToGet(val ...string) attribute.KeyValue { + return AWSDynamoDBAttributesToGetKey.StringSlice(val) +} + +// AWSDynamoDBConsistentRead returns an attribute KeyValue conforming to the +// "aws.dynamodb.consistent_read" semantic conventions. It represents the value +// of the `ConsistentRead` request parameter. +func AWSDynamoDBConsistentRead(val bool) attribute.KeyValue { + return AWSDynamoDBConsistentReadKey.Bool(val) +} + +// AWSDynamoDBConsumedCapacity returns an attribute KeyValue conforming to the +// "aws.dynamodb.consumed_capacity" semantic conventions. It represents the +// JSON-serialized value of each item in the `ConsumedCapacity` response field. +func AWSDynamoDBConsumedCapacity(val ...string) attribute.KeyValue { + return AWSDynamoDBConsumedCapacityKey.StringSlice(val) +} + +// AWSDynamoDBCount returns an attribute KeyValue conforming to the +// "aws.dynamodb.count" semantic conventions. It represents the value of the +// `Count` response parameter. +func AWSDynamoDBCount(val int) attribute.KeyValue { + return AWSDynamoDBCountKey.Int(val) +} + +// AWSDynamoDBExclusiveStartTable returns an attribute KeyValue conforming to the +// "aws.dynamodb.exclusive_start_table" semantic conventions. It represents the +// value of the `ExclusiveStartTableName` request parameter. +func AWSDynamoDBExclusiveStartTable(val string) attribute.KeyValue { + return AWSDynamoDBExclusiveStartTableKey.String(val) +} + +// AWSDynamoDBGlobalSecondaryIndexUpdates returns an attribute KeyValue +// conforming to the "aws.dynamodb.global_secondary_index_updates" semantic +// conventions. It represents the JSON-serialized value of each item in the +// `GlobalSecondaryIndexUpdates` request field. +func AWSDynamoDBGlobalSecondaryIndexUpdates(val ...string) attribute.KeyValue { + return AWSDynamoDBGlobalSecondaryIndexUpdatesKey.StringSlice(val) +} + +// AWSDynamoDBGlobalSecondaryIndexes returns an attribute KeyValue conforming to +// the "aws.dynamodb.global_secondary_indexes" semantic conventions. It +// represents the JSON-serialized value of each item of the +// `GlobalSecondaryIndexes` request field. +func AWSDynamoDBGlobalSecondaryIndexes(val ...string) attribute.KeyValue { + return AWSDynamoDBGlobalSecondaryIndexesKey.StringSlice(val) +} + +// AWSDynamoDBIndexName returns an attribute KeyValue conforming to the +// "aws.dynamodb.index_name" semantic conventions. It represents the value of the +// `IndexName` request parameter. +func AWSDynamoDBIndexName(val string) attribute.KeyValue { + return AWSDynamoDBIndexNameKey.String(val) +} + +// AWSDynamoDBItemCollectionMetrics returns an attribute KeyValue conforming to +// the "aws.dynamodb.item_collection_metrics" semantic conventions. It represents +// the JSON-serialized value of the `ItemCollectionMetrics` response field. +func AWSDynamoDBItemCollectionMetrics(val string) attribute.KeyValue { + return AWSDynamoDBItemCollectionMetricsKey.String(val) +} + +// AWSDynamoDBLimit returns an attribute KeyValue conforming to the +// "aws.dynamodb.limit" semantic conventions. It represents the value of the +// `Limit` request parameter. +func AWSDynamoDBLimit(val int) attribute.KeyValue { + return AWSDynamoDBLimitKey.Int(val) +} + +// AWSDynamoDBLocalSecondaryIndexes returns an attribute KeyValue conforming to +// the "aws.dynamodb.local_secondary_indexes" semantic conventions. It represents +// the JSON-serialized value of each item of the `LocalSecondaryIndexes` request +// field. +func AWSDynamoDBLocalSecondaryIndexes(val ...string) attribute.KeyValue { + return AWSDynamoDBLocalSecondaryIndexesKey.StringSlice(val) +} + +// AWSDynamoDBProjection returns an attribute KeyValue conforming to the +// "aws.dynamodb.projection" semantic conventions. It represents the value of the +// `ProjectionExpression` request parameter. +func AWSDynamoDBProjection(val string) attribute.KeyValue { + return AWSDynamoDBProjectionKey.String(val) +} + +// AWSDynamoDBProvisionedReadCapacity returns an attribute KeyValue conforming to +// the "aws.dynamodb.provisioned_read_capacity" semantic conventions. It +// represents the value of the `ProvisionedThroughput.ReadCapacityUnits` request +// parameter. +func AWSDynamoDBProvisionedReadCapacity(val float64) attribute.KeyValue { + return AWSDynamoDBProvisionedReadCapacityKey.Float64(val) +} + +// AWSDynamoDBProvisionedWriteCapacity returns an attribute KeyValue conforming +// to the "aws.dynamodb.provisioned_write_capacity" semantic conventions. It +// represents the value of the `ProvisionedThroughput.WriteCapacityUnits` request +// parameter. +func AWSDynamoDBProvisionedWriteCapacity(val float64) attribute.KeyValue { + return AWSDynamoDBProvisionedWriteCapacityKey.Float64(val) +} + +// AWSDynamoDBScanForward returns an attribute KeyValue conforming to the +// "aws.dynamodb.scan_forward" semantic conventions. It represents the value of +// the `ScanIndexForward` request parameter. +func AWSDynamoDBScanForward(val bool) attribute.KeyValue { + return AWSDynamoDBScanForwardKey.Bool(val) +} + +// AWSDynamoDBScannedCount returns an attribute KeyValue conforming to the +// "aws.dynamodb.scanned_count" semantic conventions. It represents the value of +// the `ScannedCount` response parameter. +func AWSDynamoDBScannedCount(val int) attribute.KeyValue { + return AWSDynamoDBScannedCountKey.Int(val) +} + +// AWSDynamoDBSegment returns an attribute KeyValue conforming to the +// "aws.dynamodb.segment" semantic conventions. It represents the value of the +// `Segment` request parameter. +func AWSDynamoDBSegment(val int) attribute.KeyValue { + return AWSDynamoDBSegmentKey.Int(val) +} + +// AWSDynamoDBSelect returns an attribute KeyValue conforming to the +// "aws.dynamodb.select" semantic conventions. It represents the value of the +// `Select` request parameter. +func AWSDynamoDBSelect(val string) attribute.KeyValue { + return AWSDynamoDBSelectKey.String(val) +} + +// AWSDynamoDBTableCount returns an attribute KeyValue conforming to the +// "aws.dynamodb.table_count" semantic conventions. It represents the number of +// items in the `TableNames` response parameter. +func AWSDynamoDBTableCount(val int) attribute.KeyValue { + return AWSDynamoDBTableCountKey.Int(val) +} + +// AWSDynamoDBTableNames returns an attribute KeyValue conforming to the +// "aws.dynamodb.table_names" semantic conventions. It represents the keys in the +// `RequestItems` object field. +func AWSDynamoDBTableNames(val ...string) attribute.KeyValue { + return AWSDynamoDBTableNamesKey.StringSlice(val) +} + +// AWSDynamoDBTotalSegments returns an attribute KeyValue conforming to the +// "aws.dynamodb.total_segments" semantic conventions. It represents the value of +// the `TotalSegments` request parameter. +func AWSDynamoDBTotalSegments(val int) attribute.KeyValue { + return AWSDynamoDBTotalSegmentsKey.Int(val) +} + +// AWSECSClusterARN returns an attribute KeyValue conforming to the +// "aws.ecs.cluster.arn" semantic conventions. It represents the ARN of an +// [ECS cluster]. +// +// [ECS cluster]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html +func AWSECSClusterARN(val string) attribute.KeyValue { + return AWSECSClusterARNKey.String(val) +} + +// AWSECSContainerARN returns an attribute KeyValue conforming to the +// "aws.ecs.container.arn" semantic conventions. It represents the Amazon +// Resource Name (ARN) of an [ECS container instance]. +// +// [ECS container instance]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_instances.html +func AWSECSContainerARN(val string) attribute.KeyValue { + return AWSECSContainerARNKey.String(val) +} + +// AWSECSTaskARN returns an attribute KeyValue conforming to the +// "aws.ecs.task.arn" semantic conventions. It represents the ARN of a running +// [ECS task]. +// +// [ECS task]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-account-settings.html#ecs-resource-ids +func AWSECSTaskARN(val string) attribute.KeyValue { + return AWSECSTaskARNKey.String(val) +} + +// AWSECSTaskFamily returns an attribute KeyValue conforming to the +// "aws.ecs.task.family" semantic conventions. It represents the family name of +// the [ECS task definition] used to create the ECS task. +// +// [ECS task definition]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html +func AWSECSTaskFamily(val string) attribute.KeyValue { + return AWSECSTaskFamilyKey.String(val) +} + +// AWSECSTaskID returns an attribute KeyValue conforming to the "aws.ecs.task.id" +// semantic conventions. It represents the ID of a running ECS task. The ID MUST +// be extracted from `task.arn`. +func AWSECSTaskID(val string) attribute.KeyValue { + return AWSECSTaskIDKey.String(val) +} + +// AWSECSTaskRevision returns an attribute KeyValue conforming to the +// "aws.ecs.task.revision" semantic conventions. It represents the revision for +// the task definition used to create the ECS task. +func AWSECSTaskRevision(val string) attribute.KeyValue { + return AWSECSTaskRevisionKey.String(val) +} + +// AWSEKSClusterARN returns an attribute KeyValue conforming to the +// "aws.eks.cluster.arn" semantic conventions. It represents the ARN of an EKS +// cluster. +func AWSEKSClusterARN(val string) attribute.KeyValue { + return AWSEKSClusterARNKey.String(val) +} + +// AWSExtendedRequestID returns an attribute KeyValue conforming to the +// "aws.extended_request_id" semantic conventions. It represents the AWS extended +// request ID as returned in the response header `x-amz-id-2`. +func AWSExtendedRequestID(val string) attribute.KeyValue { + return AWSExtendedRequestIDKey.String(val) +} + +// AWSKinesisStreamName returns an attribute KeyValue conforming to the +// "aws.kinesis.stream_name" semantic conventions. It represents the name of the +// AWS Kinesis [stream] the request refers to. Corresponds to the `--stream-name` +// parameter of the Kinesis [describe-stream] operation. +// +// [stream]: https://docs.aws.amazon.com/streams/latest/dev/introduction.html +// [describe-stream]: https://docs.aws.amazon.com/cli/latest/reference/kinesis/describe-stream.html +func AWSKinesisStreamName(val string) attribute.KeyValue { + return AWSKinesisStreamNameKey.String(val) +} + +// AWSLambdaInvokedARN returns an attribute KeyValue conforming to the +// "aws.lambda.invoked_arn" semantic conventions. It represents the full invoked +// ARN as provided on the `Context` passed to the function ( +// `Lambda-Runtime-Invoked-Function-Arn` header on the `/runtime/invocation/next` +// applicable). +func AWSLambdaInvokedARN(val string) attribute.KeyValue { + return AWSLambdaInvokedARNKey.String(val) +} + +// AWSLambdaResourceMappingID returns an attribute KeyValue conforming to the +// "aws.lambda.resource_mapping.id" semantic conventions. It represents the UUID +// of the [AWS Lambda EvenSource Mapping]. An event source is mapped to a lambda +// function. It's contents are read by Lambda and used to trigger a function. +// This isn't available in the lambda execution context or the lambda runtime +// environtment. This is going to be populated by the AWS SDK for each language +// when that UUID is present. Some of these operations are +// Create/Delete/Get/List/Update EventSourceMapping. +// +// [AWS Lambda EvenSource Mapping]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html +func AWSLambdaResourceMappingID(val string) attribute.KeyValue { + return AWSLambdaResourceMappingIDKey.String(val) +} + +// AWSLogGroupARNs returns an attribute KeyValue conforming to the +// "aws.log.group.arns" semantic conventions. It represents the Amazon Resource +// Name(s) (ARN) of the AWS log group(s). +func AWSLogGroupARNs(val ...string) attribute.KeyValue { + return AWSLogGroupARNsKey.StringSlice(val) +} + +// AWSLogGroupNames returns an attribute KeyValue conforming to the +// "aws.log.group.names" semantic conventions. It represents the name(s) of the +// AWS log group(s) an application is writing to. +func AWSLogGroupNames(val ...string) attribute.KeyValue { + return AWSLogGroupNamesKey.StringSlice(val) +} + +// AWSLogStreamARNs returns an attribute KeyValue conforming to the +// "aws.log.stream.arns" semantic conventions. It represents the ARN(s) of the +// AWS log stream(s). +func AWSLogStreamARNs(val ...string) attribute.KeyValue { + return AWSLogStreamARNsKey.StringSlice(val) +} + +// AWSLogStreamNames returns an attribute KeyValue conforming to the +// "aws.log.stream.names" semantic conventions. It represents the name(s) of the +// AWS log stream(s) an application is writing to. +func AWSLogStreamNames(val ...string) attribute.KeyValue { + return AWSLogStreamNamesKey.StringSlice(val) +} + +// AWSRequestID returns an attribute KeyValue conforming to the "aws.request_id" +// semantic conventions. It represents the AWS request ID as returned in the +// response headers `x-amzn-requestid`, `x-amzn-request-id` or `x-amz-request-id` +// . +func AWSRequestID(val string) attribute.KeyValue { + return AWSRequestIDKey.String(val) +} + +// AWSS3Bucket returns an attribute KeyValue conforming to the "aws.s3.bucket" +// semantic conventions. It represents the S3 bucket name the request refers to. +// Corresponds to the `--bucket` parameter of the [S3 API] operations. +// +// [S3 API]: https://docs.aws.amazon.com/cli/latest/reference/s3api/index.html +func AWSS3Bucket(val string) attribute.KeyValue { + return AWSS3BucketKey.String(val) +} + +// AWSS3CopySource returns an attribute KeyValue conforming to the +// "aws.s3.copy_source" semantic conventions. It represents the source object (in +// the form `bucket`/`key`) for the copy operation. +func AWSS3CopySource(val string) attribute.KeyValue { + return AWSS3CopySourceKey.String(val) +} + +// AWSS3Delete returns an attribute KeyValue conforming to the "aws.s3.delete" +// semantic conventions. It represents the delete request container that +// specifies the objects to be deleted. +func AWSS3Delete(val string) attribute.KeyValue { + return AWSS3DeleteKey.String(val) +} + +// AWSS3Key returns an attribute KeyValue conforming to the "aws.s3.key" semantic +// conventions. It represents the S3 object key the request refers to. +// Corresponds to the `--key` parameter of the [S3 API] operations. +// +// [S3 API]: https://docs.aws.amazon.com/cli/latest/reference/s3api/index.html +func AWSS3Key(val string) attribute.KeyValue { + return AWSS3KeyKey.String(val) +} + +// AWSS3PartNumber returns an attribute KeyValue conforming to the +// "aws.s3.part_number" semantic conventions. It represents the part number of +// the part being uploaded in a multipart-upload operation. This is a positive +// integer between 1 and 10,000. +func AWSS3PartNumber(val int) attribute.KeyValue { + return AWSS3PartNumberKey.Int(val) +} + +// AWSS3UploadID returns an attribute KeyValue conforming to the +// "aws.s3.upload_id" semantic conventions. It represents the upload ID that +// identifies the multipart upload. +func AWSS3UploadID(val string) attribute.KeyValue { + return AWSS3UploadIDKey.String(val) +} + +// AWSSecretsmanagerSecretARN returns an attribute KeyValue conforming to the +// "aws.secretsmanager.secret.arn" semantic conventions. It represents the ARN of +// the Secret stored in the Secrets Mangger. +func AWSSecretsmanagerSecretARN(val string) attribute.KeyValue { + return AWSSecretsmanagerSecretARNKey.String(val) +} + +// AWSSNSTopicARN returns an attribute KeyValue conforming to the +// "aws.sns.topic.arn" semantic conventions. It represents the ARN of the AWS SNS +// Topic. An Amazon SNS [topic] is a logical access point that acts as a +// communication channel. +// +// [topic]: https://docs.aws.amazon.com/sns/latest/dg/sns-create-topic.html +func AWSSNSTopicARN(val string) attribute.KeyValue { + return AWSSNSTopicARNKey.String(val) +} + +// AWSSQSQueueURL returns an attribute KeyValue conforming to the +// "aws.sqs.queue.url" semantic conventions. It represents the URL of the AWS SQS +// Queue. It's a unique identifier for a queue in Amazon Simple Queue Service +// (SQS) and is used to access the queue and perform actions on it. +func AWSSQSQueueURL(val string) attribute.KeyValue { + return AWSSQSQueueURLKey.String(val) +} + +// AWSStepFunctionsActivityARN returns an attribute KeyValue conforming to the +// "aws.step_functions.activity.arn" semantic conventions. It represents the ARN +// of the AWS Step Functions Activity. +func AWSStepFunctionsActivityARN(val string) attribute.KeyValue { + return AWSStepFunctionsActivityARNKey.String(val) +} + +// AWSStepFunctionsStateMachineARN returns an attribute KeyValue conforming to +// the "aws.step_functions.state_machine.arn" semantic conventions. It represents +// the ARN of the AWS Step Functions State Machine. +func AWSStepFunctionsStateMachineARN(val string) attribute.KeyValue { + return AWSStepFunctionsStateMachineARNKey.String(val) +} + +// Enum values for aws.ecs.launchtype +var ( + // Amazon EC2 + // Stability: development + AWSECSLaunchtypeEC2 = AWSECSLaunchtypeKey.String("ec2") + // Amazon Fargate + // Stability: development + AWSECSLaunchtypeFargate = AWSECSLaunchtypeKey.String("fargate") +) + +// Namespace: azure +const ( + // AzureClientIDKey is the attribute Key conforming to the "azure.client.id" + // semantic conventions. It represents the unique identifier of the client + // instance. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "3ba4827d-4422-483f-b59f-85b74211c11d", "storage-client-1" + AzureClientIDKey = attribute.Key("azure.client.id") + + // AzureCosmosDBConnectionModeKey is the attribute Key conforming to the + // "azure.cosmosdb.connection.mode" semantic conventions. It represents the + // cosmos client connection mode. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + AzureCosmosDBConnectionModeKey = attribute.Key("azure.cosmosdb.connection.mode") + + // AzureCosmosDBConsistencyLevelKey is the attribute Key conforming to the + // "azure.cosmosdb.consistency.level" semantic conventions. It represents the + // account or request [consistency level]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Eventual", "ConsistentPrefix", "BoundedStaleness", "Strong", + // "Session" + // + // [consistency level]: https://learn.microsoft.com/azure/cosmos-db/consistency-levels + AzureCosmosDBConsistencyLevelKey = attribute.Key("azure.cosmosdb.consistency.level") + + // AzureCosmosDBOperationContactedRegionsKey is the attribute Key conforming to + // the "azure.cosmosdb.operation.contacted_regions" semantic conventions. It + // represents the list of regions contacted during operation in the order that + // they were contacted. If there is more than one region listed, it indicates + // that the operation was performed on multiple regions i.e. cross-regional + // call. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "North Central US", "Australia East", "Australia Southeast" + // Note: Region name matches the format of `displayName` in [Azure Location API] + // + // [Azure Location API]: https://learn.microsoft.com/rest/api/resources/subscriptions/list-locations + AzureCosmosDBOperationContactedRegionsKey = attribute.Key("azure.cosmosdb.operation.contacted_regions") + + // AzureCosmosDBOperationRequestChargeKey is the attribute Key conforming to the + // "azure.cosmosdb.operation.request_charge" semantic conventions. It represents + // the number of request units consumed by the operation. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 46.18, 1.0 + AzureCosmosDBOperationRequestChargeKey = attribute.Key("azure.cosmosdb.operation.request_charge") + + // AzureCosmosDBRequestBodySizeKey is the attribute Key conforming to the + // "azure.cosmosdb.request.body.size" semantic conventions. It represents the + // request payload size in bytes. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + AzureCosmosDBRequestBodySizeKey = attribute.Key("azure.cosmosdb.request.body.size") + + // AzureCosmosDBResponseSubStatusCodeKey is the attribute Key conforming to the + // "azure.cosmosdb.response.sub_status_code" semantic conventions. It represents + // the cosmos DB sub status code. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1000, 1002 + AzureCosmosDBResponseSubStatusCodeKey = attribute.Key("azure.cosmosdb.response.sub_status_code") + + // AzureResourceProviderNamespaceKey is the attribute Key conforming to the + // "azure.resource_provider.namespace" semantic conventions. It represents the + // [Azure Resource Provider Namespace] as recognized by the client. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Microsoft.Storage", "Microsoft.KeyVault", "Microsoft.ServiceBus" + // + // [Azure Resource Provider Namespace]: https://learn.microsoft.com/azure/azure-resource-manager/management/azure-services-resource-providers + AzureResourceProviderNamespaceKey = attribute.Key("azure.resource_provider.namespace") + + // AzureServiceRequestIDKey is the attribute Key conforming to the + // "azure.service.request.id" semantic conventions. It represents the unique + // identifier of the service request. It's generated by the Azure service and + // returned with the response. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "00000000-0000-0000-0000-000000000000" + AzureServiceRequestIDKey = attribute.Key("azure.service.request.id") +) + +// AzureClientID returns an attribute KeyValue conforming to the +// "azure.client.id" semantic conventions. It represents the unique identifier of +// the client instance. +func AzureClientID(val string) attribute.KeyValue { + return AzureClientIDKey.String(val) +} + +// AzureCosmosDBOperationContactedRegions returns an attribute KeyValue +// conforming to the "azure.cosmosdb.operation.contacted_regions" semantic +// conventions. It represents the list of regions contacted during operation in +// the order that they were contacted. If there is more than one region listed, +// it indicates that the operation was performed on multiple regions i.e. +// cross-regional call. +func AzureCosmosDBOperationContactedRegions(val ...string) attribute.KeyValue { + return AzureCosmosDBOperationContactedRegionsKey.StringSlice(val) +} + +// AzureCosmosDBOperationRequestCharge returns an attribute KeyValue conforming +// to the "azure.cosmosdb.operation.request_charge" semantic conventions. It +// represents the number of request units consumed by the operation. +func AzureCosmosDBOperationRequestCharge(val float64) attribute.KeyValue { + return AzureCosmosDBOperationRequestChargeKey.Float64(val) +} + +// AzureCosmosDBRequestBodySize returns an attribute KeyValue conforming to the +// "azure.cosmosdb.request.body.size" semantic conventions. It represents the +// request payload size in bytes. +func AzureCosmosDBRequestBodySize(val int) attribute.KeyValue { + return AzureCosmosDBRequestBodySizeKey.Int(val) +} + +// AzureCosmosDBResponseSubStatusCode returns an attribute KeyValue conforming to +// the "azure.cosmosdb.response.sub_status_code" semantic conventions. It +// represents the cosmos DB sub status code. +func AzureCosmosDBResponseSubStatusCode(val int) attribute.KeyValue { + return AzureCosmosDBResponseSubStatusCodeKey.Int(val) +} + +// AzureResourceProviderNamespace returns an attribute KeyValue conforming to the +// "azure.resource_provider.namespace" semantic conventions. It represents the +// [Azure Resource Provider Namespace] as recognized by the client. +// +// [Azure Resource Provider Namespace]: https://learn.microsoft.com/azure/azure-resource-manager/management/azure-services-resource-providers +func AzureResourceProviderNamespace(val string) attribute.KeyValue { + return AzureResourceProviderNamespaceKey.String(val) +} + +// AzureServiceRequestID returns an attribute KeyValue conforming to the +// "azure.service.request.id" semantic conventions. It represents the unique +// identifier of the service request. It's generated by the Azure service and +// returned with the response. +func AzureServiceRequestID(val string) attribute.KeyValue { + return AzureServiceRequestIDKey.String(val) +} + +// Enum values for azure.cosmosdb.connection.mode +var ( + // Gateway (HTTP) connection. + // Stability: development + AzureCosmosDBConnectionModeGateway = AzureCosmosDBConnectionModeKey.String("gateway") + // Direct connection. + // Stability: development + AzureCosmosDBConnectionModeDirect = AzureCosmosDBConnectionModeKey.String("direct") +) + +// Enum values for azure.cosmosdb.consistency.level +var ( + // Strong + // Stability: development + AzureCosmosDBConsistencyLevelStrong = AzureCosmosDBConsistencyLevelKey.String("Strong") + // Bounded Staleness + // Stability: development + AzureCosmosDBConsistencyLevelBoundedStaleness = AzureCosmosDBConsistencyLevelKey.String("BoundedStaleness") + // Session + // Stability: development + AzureCosmosDBConsistencyLevelSession = AzureCosmosDBConsistencyLevelKey.String("Session") + // Eventual + // Stability: development + AzureCosmosDBConsistencyLevelEventual = AzureCosmosDBConsistencyLevelKey.String("Eventual") + // Consistent Prefix + // Stability: development + AzureCosmosDBConsistencyLevelConsistentPrefix = AzureCosmosDBConsistencyLevelKey.String("ConsistentPrefix") +) + +// Namespace: browser +const ( + // BrowserBrandsKey is the attribute Key conforming to the "browser.brands" + // semantic conventions. It represents the array of brand name and version + // separated by a space. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: " Not A;Brand 99", "Chromium 99", "Chrome 99" + // Note: This value is intended to be taken from the [UA client hints API] ( + // `navigator.userAgentData.brands`). + // + // [UA client hints API]: https://wicg.github.io/ua-client-hints/#interface + BrowserBrandsKey = attribute.Key("browser.brands") + + // BrowserLanguageKey is the attribute Key conforming to the "browser.language" + // semantic conventions. It represents the preferred language of the user using + // the browser. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "en", "en-US", "fr", "fr-FR" + // Note: This value is intended to be taken from the Navigator API + // `navigator.language`. + BrowserLanguageKey = attribute.Key("browser.language") + + // BrowserMobileKey is the attribute Key conforming to the "browser.mobile" + // semantic conventions. It represents a boolean that is true if the browser is + // running on a mobile device. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: This value is intended to be taken from the [UA client hints API] ( + // `navigator.userAgentData.mobile`). If unavailable, this attribute SHOULD be + // left unset. + // + // [UA client hints API]: https://wicg.github.io/ua-client-hints/#interface + BrowserMobileKey = attribute.Key("browser.mobile") + + // BrowserPlatformKey is the attribute Key conforming to the "browser.platform" + // semantic conventions. It represents the platform on which the browser is + // running. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Windows", "macOS", "Android" + // Note: This value is intended to be taken from the [UA client hints API] ( + // `navigator.userAgentData.platform`). If unavailable, the legacy + // `navigator.platform` API SHOULD NOT be used instead and this attribute SHOULD + // be left unset in order for the values to be consistent. + // The list of possible values is defined in the + // [W3C User-Agent Client Hints specification]. Note that some (but not all) of + // these values can overlap with values in the + // [`os.type` and `os.name` attributes]. However, for consistency, the values in + // the `browser.platform` attribute should capture the exact value that the user + // agent provides. + // + // [UA client hints API]: https://wicg.github.io/ua-client-hints/#interface + // [W3C User-Agent Client Hints specification]: https://wicg.github.io/ua-client-hints/#sec-ch-ua-platform + // [`os.type` and `os.name` attributes]: ./os.md + BrowserPlatformKey = attribute.Key("browser.platform") +) + +// BrowserBrands returns an attribute KeyValue conforming to the "browser.brands" +// semantic conventions. It represents the array of brand name and version +// separated by a space. +func BrowserBrands(val ...string) attribute.KeyValue { + return BrowserBrandsKey.StringSlice(val) +} + +// BrowserLanguage returns an attribute KeyValue conforming to the +// "browser.language" semantic conventions. It represents the preferred language +// of the user using the browser. +func BrowserLanguage(val string) attribute.KeyValue { + return BrowserLanguageKey.String(val) +} + +// BrowserMobile returns an attribute KeyValue conforming to the "browser.mobile" +// semantic conventions. It represents a boolean that is true if the browser is +// running on a mobile device. +func BrowserMobile(val bool) attribute.KeyValue { + return BrowserMobileKey.Bool(val) +} + +// BrowserPlatform returns an attribute KeyValue conforming to the +// "browser.platform" semantic conventions. It represents the platform on which +// the browser is running. +func BrowserPlatform(val string) attribute.KeyValue { + return BrowserPlatformKey.String(val) +} + +// Namespace: cassandra +const ( + // CassandraConsistencyLevelKey is the attribute Key conforming to the + // "cassandra.consistency.level" semantic conventions. It represents the + // consistency level of the query. Based on consistency values from [CQL]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // + // [CQL]: https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html + CassandraConsistencyLevelKey = attribute.Key("cassandra.consistency.level") + + // CassandraCoordinatorDCKey is the attribute Key conforming to the + // "cassandra.coordinator.dc" semantic conventions. It represents the data + // center of the coordinating node for a query. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: us-west-2 + CassandraCoordinatorDCKey = attribute.Key("cassandra.coordinator.dc") + + // CassandraCoordinatorIDKey is the attribute Key conforming to the + // "cassandra.coordinator.id" semantic conventions. It represents the ID of the + // coordinating node for a query. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: be13faa2-8574-4d71-926d-27f16cf8a7af + CassandraCoordinatorIDKey = attribute.Key("cassandra.coordinator.id") + + // CassandraPageSizeKey is the attribute Key conforming to the + // "cassandra.page.size" semantic conventions. It represents the fetch size used + // for paging, i.e. how many rows will be returned at once. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 5000 + CassandraPageSizeKey = attribute.Key("cassandra.page.size") + + // CassandraQueryIdempotentKey is the attribute Key conforming to the + // "cassandra.query.idempotent" semantic conventions. It represents the whether + // or not the query is idempotent. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + CassandraQueryIdempotentKey = attribute.Key("cassandra.query.idempotent") + + // CassandraSpeculativeExecutionCountKey is the attribute Key conforming to the + // "cassandra.speculative_execution.count" semantic conventions. It represents + // the number of times a query was speculatively executed. Not set or `0` if the + // query was not executed speculatively. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 0, 2 + CassandraSpeculativeExecutionCountKey = attribute.Key("cassandra.speculative_execution.count") +) + +// CassandraCoordinatorDC returns an attribute KeyValue conforming to the +// "cassandra.coordinator.dc" semantic conventions. It represents the data center +// of the coordinating node for a query. +func CassandraCoordinatorDC(val string) attribute.KeyValue { + return CassandraCoordinatorDCKey.String(val) +} + +// CassandraCoordinatorID returns an attribute KeyValue conforming to the +// "cassandra.coordinator.id" semantic conventions. It represents the ID of the +// coordinating node for a query. +func CassandraCoordinatorID(val string) attribute.KeyValue { + return CassandraCoordinatorIDKey.String(val) +} + +// CassandraPageSize returns an attribute KeyValue conforming to the +// "cassandra.page.size" semantic conventions. It represents the fetch size used +// for paging, i.e. how many rows will be returned at once. +func CassandraPageSize(val int) attribute.KeyValue { + return CassandraPageSizeKey.Int(val) +} + +// CassandraQueryIdempotent returns an attribute KeyValue conforming to the +// "cassandra.query.idempotent" semantic conventions. It represents the whether +// or not the query is idempotent. +func CassandraQueryIdempotent(val bool) attribute.KeyValue { + return CassandraQueryIdempotentKey.Bool(val) +} + +// CassandraSpeculativeExecutionCount returns an attribute KeyValue conforming to +// the "cassandra.speculative_execution.count" semantic conventions. It +// represents the number of times a query was speculatively executed. Not set or +// `0` if the query was not executed speculatively. +func CassandraSpeculativeExecutionCount(val int) attribute.KeyValue { + return CassandraSpeculativeExecutionCountKey.Int(val) +} + +// Enum values for cassandra.consistency.level +var ( + // All + // Stability: development + CassandraConsistencyLevelAll = CassandraConsistencyLevelKey.String("all") + // Each Quorum + // Stability: development + CassandraConsistencyLevelEachQuorum = CassandraConsistencyLevelKey.String("each_quorum") + // Quorum + // Stability: development + CassandraConsistencyLevelQuorum = CassandraConsistencyLevelKey.String("quorum") + // Local Quorum + // Stability: development + CassandraConsistencyLevelLocalQuorum = CassandraConsistencyLevelKey.String("local_quorum") + // One + // Stability: development + CassandraConsistencyLevelOne = CassandraConsistencyLevelKey.String("one") + // Two + // Stability: development + CassandraConsistencyLevelTwo = CassandraConsistencyLevelKey.String("two") + // Three + // Stability: development + CassandraConsistencyLevelThree = CassandraConsistencyLevelKey.String("three") + // Local One + // Stability: development + CassandraConsistencyLevelLocalOne = CassandraConsistencyLevelKey.String("local_one") + // Any + // Stability: development + CassandraConsistencyLevelAny = CassandraConsistencyLevelKey.String("any") + // Serial + // Stability: development + CassandraConsistencyLevelSerial = CassandraConsistencyLevelKey.String("serial") + // Local Serial + // Stability: development + CassandraConsistencyLevelLocalSerial = CassandraConsistencyLevelKey.String("local_serial") +) + +// Namespace: cicd +const ( + // CICDPipelineActionNameKey is the attribute Key conforming to the + // "cicd.pipeline.action.name" semantic conventions. It represents the kind of + // action a pipeline run is performing. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "BUILD", "RUN", "SYNC" + CICDPipelineActionNameKey = attribute.Key("cicd.pipeline.action.name") + + // CICDPipelineNameKey is the attribute Key conforming to the + // "cicd.pipeline.name" semantic conventions. It represents the human readable + // name of the pipeline within a CI/CD system. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Build and Test", "Lint", "Deploy Go Project", + // "deploy_to_environment" + CICDPipelineNameKey = attribute.Key("cicd.pipeline.name") + + // CICDPipelineResultKey is the attribute Key conforming to the + // "cicd.pipeline.result" semantic conventions. It represents the result of a + // pipeline run. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "success", "failure", "timeout", "skipped" + CICDPipelineResultKey = attribute.Key("cicd.pipeline.result") + + // CICDPipelineRunIDKey is the attribute Key conforming to the + // "cicd.pipeline.run.id" semantic conventions. It represents the unique + // identifier of a pipeline run within a CI/CD system. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "120912" + CICDPipelineRunIDKey = attribute.Key("cicd.pipeline.run.id") + + // CICDPipelineRunStateKey is the attribute Key conforming to the + // "cicd.pipeline.run.state" semantic conventions. It represents the pipeline + // run goes through these states during its lifecycle. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "pending", "executing", "finalizing" + CICDPipelineRunStateKey = attribute.Key("cicd.pipeline.run.state") + + // CICDPipelineRunURLFullKey is the attribute Key conforming to the + // "cicd.pipeline.run.url.full" semantic conventions. It represents the [URL] of + // the pipeline run, providing the complete address in order to locate and + // identify the pipeline run. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "https://github.com/open-telemetry/semantic-conventions/actions/runs/9753949763?pr=1075" + // + // [URL]: https://wikipedia.org/wiki/URL + CICDPipelineRunURLFullKey = attribute.Key("cicd.pipeline.run.url.full") + + // CICDPipelineTaskNameKey is the attribute Key conforming to the + // "cicd.pipeline.task.name" semantic conventions. It represents the human + // readable name of a task within a pipeline. Task here most closely aligns with + // a [computing process] in a pipeline. Other terms for tasks include commands, + // steps, and procedures. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Run GoLang Linter", "Go Build", "go-test", "deploy_binary" + // + // [computing process]: https://wikipedia.org/wiki/Pipeline_(computing) + CICDPipelineTaskNameKey = attribute.Key("cicd.pipeline.task.name") + + // CICDPipelineTaskRunIDKey is the attribute Key conforming to the + // "cicd.pipeline.task.run.id" semantic conventions. It represents the unique + // identifier of a task run within a pipeline. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "12097" + CICDPipelineTaskRunIDKey = attribute.Key("cicd.pipeline.task.run.id") + + // CICDPipelineTaskRunResultKey is the attribute Key conforming to the + // "cicd.pipeline.task.run.result" semantic conventions. It represents the + // result of a task run. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "success", "failure", "timeout", "skipped" + CICDPipelineTaskRunResultKey = attribute.Key("cicd.pipeline.task.run.result") + + // CICDPipelineTaskRunURLFullKey is the attribute Key conforming to the + // "cicd.pipeline.task.run.url.full" semantic conventions. It represents the + // [URL] of the pipeline task run, providing the complete address in order to + // locate and identify the pipeline task run. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "https://github.com/open-telemetry/semantic-conventions/actions/runs/9753949763/job/26920038674?pr=1075" + // + // [URL]: https://wikipedia.org/wiki/URL + CICDPipelineTaskRunURLFullKey = attribute.Key("cicd.pipeline.task.run.url.full") + + // CICDPipelineTaskTypeKey is the attribute Key conforming to the + // "cicd.pipeline.task.type" semantic conventions. It represents the type of the + // task within a pipeline. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "build", "test", "deploy" + CICDPipelineTaskTypeKey = attribute.Key("cicd.pipeline.task.type") + + // CICDSystemComponentKey is the attribute Key conforming to the + // "cicd.system.component" semantic conventions. It represents the name of a + // component of the CICD system. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "controller", "scheduler", "agent" + CICDSystemComponentKey = attribute.Key("cicd.system.component") + + // CICDWorkerIDKey is the attribute Key conforming to the "cicd.worker.id" + // semantic conventions. It represents the unique identifier of a worker within + // a CICD system. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "abc123", "10.0.1.2", "controller" + CICDWorkerIDKey = attribute.Key("cicd.worker.id") + + // CICDWorkerNameKey is the attribute Key conforming to the "cicd.worker.name" + // semantic conventions. It represents the name of a worker within a CICD + // system. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "agent-abc", "controller", "Ubuntu LTS" + CICDWorkerNameKey = attribute.Key("cicd.worker.name") + + // CICDWorkerStateKey is the attribute Key conforming to the "cicd.worker.state" + // semantic conventions. It represents the state of a CICD worker / agent. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "idle", "busy", "down" + CICDWorkerStateKey = attribute.Key("cicd.worker.state") + + // CICDWorkerURLFullKey is the attribute Key conforming to the + // "cicd.worker.url.full" semantic conventions. It represents the [URL] of the + // worker, providing the complete address in order to locate and identify the + // worker. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "https://cicd.example.org/worker/abc123" + // + // [URL]: https://wikipedia.org/wiki/URL + CICDWorkerURLFullKey = attribute.Key("cicd.worker.url.full") +) + +// CICDPipelineName returns an attribute KeyValue conforming to the +// "cicd.pipeline.name" semantic conventions. It represents the human readable +// name of the pipeline within a CI/CD system. +func CICDPipelineName(val string) attribute.KeyValue { + return CICDPipelineNameKey.String(val) +} + +// CICDPipelineRunID returns an attribute KeyValue conforming to the +// "cicd.pipeline.run.id" semantic conventions. It represents the unique +// identifier of a pipeline run within a CI/CD system. +func CICDPipelineRunID(val string) attribute.KeyValue { + return CICDPipelineRunIDKey.String(val) +} + +// CICDPipelineRunURLFull returns an attribute KeyValue conforming to the +// "cicd.pipeline.run.url.full" semantic conventions. It represents the [URL] of +// the pipeline run, providing the complete address in order to locate and +// identify the pipeline run. +// +// [URL]: https://wikipedia.org/wiki/URL +func CICDPipelineRunURLFull(val string) attribute.KeyValue { + return CICDPipelineRunURLFullKey.String(val) +} + +// CICDPipelineTaskName returns an attribute KeyValue conforming to the +// "cicd.pipeline.task.name" semantic conventions. It represents the human +// readable name of a task within a pipeline. Task here most closely aligns with +// a [computing process] in a pipeline. Other terms for tasks include commands, +// steps, and procedures. +// +// [computing process]: https://wikipedia.org/wiki/Pipeline_(computing) +func CICDPipelineTaskName(val string) attribute.KeyValue { + return CICDPipelineTaskNameKey.String(val) +} + +// CICDPipelineTaskRunID returns an attribute KeyValue conforming to the +// "cicd.pipeline.task.run.id" semantic conventions. It represents the unique +// identifier of a task run within a pipeline. +func CICDPipelineTaskRunID(val string) attribute.KeyValue { + return CICDPipelineTaskRunIDKey.String(val) +} + +// CICDPipelineTaskRunURLFull returns an attribute KeyValue conforming to the +// "cicd.pipeline.task.run.url.full" semantic conventions. It represents the +// [URL] of the pipeline task run, providing the complete address in order to +// locate and identify the pipeline task run. +// +// [URL]: https://wikipedia.org/wiki/URL +func CICDPipelineTaskRunURLFull(val string) attribute.KeyValue { + return CICDPipelineTaskRunURLFullKey.String(val) +} + +// CICDSystemComponent returns an attribute KeyValue conforming to the +// "cicd.system.component" semantic conventions. It represents the name of a +// component of the CICD system. +func CICDSystemComponent(val string) attribute.KeyValue { + return CICDSystemComponentKey.String(val) +} + +// CICDWorkerID returns an attribute KeyValue conforming to the "cicd.worker.id" +// semantic conventions. It represents the unique identifier of a worker within a +// CICD system. +func CICDWorkerID(val string) attribute.KeyValue { + return CICDWorkerIDKey.String(val) +} + +// CICDWorkerName returns an attribute KeyValue conforming to the +// "cicd.worker.name" semantic conventions. It represents the name of a worker +// within a CICD system. +func CICDWorkerName(val string) attribute.KeyValue { + return CICDWorkerNameKey.String(val) +} + +// CICDWorkerURLFull returns an attribute KeyValue conforming to the +// "cicd.worker.url.full" semantic conventions. It represents the [URL] of the +// worker, providing the complete address in order to locate and identify the +// worker. +// +// [URL]: https://wikipedia.org/wiki/URL +func CICDWorkerURLFull(val string) attribute.KeyValue { + return CICDWorkerURLFullKey.String(val) +} + +// Enum values for cicd.pipeline.action.name +var ( + // The pipeline run is executing a build. + // Stability: development + CICDPipelineActionNameBuild = CICDPipelineActionNameKey.String("BUILD") + // The pipeline run is executing. + // Stability: development + CICDPipelineActionNameRun = CICDPipelineActionNameKey.String("RUN") + // The pipeline run is executing a sync. + // Stability: development + CICDPipelineActionNameSync = CICDPipelineActionNameKey.String("SYNC") +) + +// Enum values for cicd.pipeline.result +var ( + // The pipeline run finished successfully. + // Stability: development + CICDPipelineResultSuccess = CICDPipelineResultKey.String("success") + // The pipeline run did not finish successfully, eg. due to a compile error or a + // failing test. Such failures are usually detected by non-zero exit codes of + // the tools executed in the pipeline run. + // Stability: development + CICDPipelineResultFailure = CICDPipelineResultKey.String("failure") + // The pipeline run failed due to an error in the CICD system, eg. due to the + // worker being killed. + // Stability: development + CICDPipelineResultError = CICDPipelineResultKey.String("error") + // A timeout caused the pipeline run to be interrupted. + // Stability: development + CICDPipelineResultTimeout = CICDPipelineResultKey.String("timeout") + // The pipeline run was cancelled, eg. by a user manually cancelling the + // pipeline run. + // Stability: development + CICDPipelineResultCancellation = CICDPipelineResultKey.String("cancellation") + // The pipeline run was skipped, eg. due to a precondition not being met. + // Stability: development + CICDPipelineResultSkip = CICDPipelineResultKey.String("skip") +) + +// Enum values for cicd.pipeline.run.state +var ( + // The run pending state spans from the event triggering the pipeline run until + // the execution of the run starts (eg. time spent in a queue, provisioning + // agents, creating run resources). + // + // Stability: development + CICDPipelineRunStatePending = CICDPipelineRunStateKey.String("pending") + // The executing state spans the execution of any run tasks (eg. build, test). + // Stability: development + CICDPipelineRunStateExecuting = CICDPipelineRunStateKey.String("executing") + // The finalizing state spans from when the run has finished executing (eg. + // cleanup of run resources). + // Stability: development + CICDPipelineRunStateFinalizing = CICDPipelineRunStateKey.String("finalizing") +) + +// Enum values for cicd.pipeline.task.run.result +var ( + // The task run finished successfully. + // Stability: development + CICDPipelineTaskRunResultSuccess = CICDPipelineTaskRunResultKey.String("success") + // The task run did not finish successfully, eg. due to a compile error or a + // failing test. Such failures are usually detected by non-zero exit codes of + // the tools executed in the task run. + // Stability: development + CICDPipelineTaskRunResultFailure = CICDPipelineTaskRunResultKey.String("failure") + // The task run failed due to an error in the CICD system, eg. due to the worker + // being killed. + // Stability: development + CICDPipelineTaskRunResultError = CICDPipelineTaskRunResultKey.String("error") + // A timeout caused the task run to be interrupted. + // Stability: development + CICDPipelineTaskRunResultTimeout = CICDPipelineTaskRunResultKey.String("timeout") + // The task run was cancelled, eg. by a user manually cancelling the task run. + // Stability: development + CICDPipelineTaskRunResultCancellation = CICDPipelineTaskRunResultKey.String("cancellation") + // The task run was skipped, eg. due to a precondition not being met. + // Stability: development + CICDPipelineTaskRunResultSkip = CICDPipelineTaskRunResultKey.String("skip") +) + +// Enum values for cicd.pipeline.task.type +var ( + // build + // Stability: development + CICDPipelineTaskTypeBuild = CICDPipelineTaskTypeKey.String("build") + // test + // Stability: development + CICDPipelineTaskTypeTest = CICDPipelineTaskTypeKey.String("test") + // deploy + // Stability: development + CICDPipelineTaskTypeDeploy = CICDPipelineTaskTypeKey.String("deploy") +) + +// Enum values for cicd.worker.state +var ( + // The worker is not performing work for the CICD system. It is available to the + // CICD system to perform work on (online / idle). + // Stability: development + CICDWorkerStateAvailable = CICDWorkerStateKey.String("available") + // The worker is performing work for the CICD system. + // Stability: development + CICDWorkerStateBusy = CICDWorkerStateKey.String("busy") + // The worker is not available to the CICD system (disconnected / down). + // Stability: development + CICDWorkerStateOffline = CICDWorkerStateKey.String("offline") +) + +// Namespace: client +const ( + // ClientAddressKey is the attribute Key conforming to the "client.address" + // semantic conventions. It represents the client address - domain name if + // available without reverse DNS lookup; otherwise, IP address or Unix domain + // socket name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "client.example.com", "10.1.2.80", "/tmp/my.sock" + // Note: When observed from the server side, and when communicating through an + // intermediary, `client.address` SHOULD represent the client address behind any + // intermediaries, for example proxies, if it's available. + ClientAddressKey = attribute.Key("client.address") + + // ClientPortKey is the attribute Key conforming to the "client.port" semantic + // conventions. It represents the client port number. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: 65123 + // Note: When observed from the server side, and when communicating through an + // intermediary, `client.port` SHOULD represent the client port behind any + // intermediaries, for example proxies, if it's available. + ClientPortKey = attribute.Key("client.port") +) + +// ClientAddress returns an attribute KeyValue conforming to the "client.address" +// semantic conventions. It represents the client address - domain name if +// available without reverse DNS lookup; otherwise, IP address or Unix domain +// socket name. +func ClientAddress(val string) attribute.KeyValue { + return ClientAddressKey.String(val) +} + +// ClientPort returns an attribute KeyValue conforming to the "client.port" +// semantic conventions. It represents the client port number. +func ClientPort(val int) attribute.KeyValue { + return ClientPortKey.Int(val) +} + +// Namespace: cloud +const ( + // CloudAccountIDKey is the attribute Key conforming to the "cloud.account.id" + // semantic conventions. It represents the cloud account ID the resource is + // assigned to. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "111111111111", "opentelemetry" + CloudAccountIDKey = attribute.Key("cloud.account.id") + + // CloudAvailabilityZoneKey is the attribute Key conforming to the + // "cloud.availability_zone" semantic conventions. It represents the cloud + // regions often have multiple, isolated locations known as zones to increase + // availability. Availability zone represents the zone where the resource is + // running. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "us-east-1c" + // Note: Availability zones are called "zones" on Alibaba Cloud and Google + // Cloud. + CloudAvailabilityZoneKey = attribute.Key("cloud.availability_zone") + + // CloudPlatformKey is the attribute Key conforming to the "cloud.platform" + // semantic conventions. It represents the cloud platform in use. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: The prefix of the service SHOULD match the one specified in + // `cloud.provider`. + CloudPlatformKey = attribute.Key("cloud.platform") + + // CloudProviderKey is the attribute Key conforming to the "cloud.provider" + // semantic conventions. It represents the name of the cloud provider. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + CloudProviderKey = attribute.Key("cloud.provider") + + // CloudRegionKey is the attribute Key conforming to the "cloud.region" semantic + // conventions. It represents the geographical region within a cloud provider. + // When associated with a resource, this attribute specifies the region where + // the resource operates. When calling services or APIs deployed on a cloud, + // this attribute identifies the region where the called destination is + // deployed. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "us-central1", "us-east-1" + // Note: Refer to your provider's docs to see the available regions, for example + // [Alibaba Cloud regions], [AWS regions], [Azure regions], + // [Google Cloud regions], or [Tencent Cloud regions]. + // + // [Alibaba Cloud regions]: https://www.alibabacloud.com/help/doc-detail/40654.htm + // [AWS regions]: https://aws.amazon.com/about-aws/global-infrastructure/regions_az/ + // [Azure regions]: https://azure.microsoft.com/global-infrastructure/geographies/ + // [Google Cloud regions]: https://cloud.google.com/about/locations + // [Tencent Cloud regions]: https://www.tencentcloud.com/document/product/213/6091 + CloudRegionKey = attribute.Key("cloud.region") + + // CloudResourceIDKey is the attribute Key conforming to the "cloud.resource_id" + // semantic conventions. It represents the cloud provider-specific native + // identifier of the monitored cloud resource (e.g. an [ARN] on AWS, a + // [fully qualified resource ID] on Azure, a [full resource name] on GCP). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "arn:aws:lambda:REGION:ACCOUNT_ID:function:my-function", + // "//run.googleapis.com/projects/PROJECT_ID/locations/LOCATION_ID/services/SERVICE_ID", + // "/subscriptions//resourceGroups/ + // /providers/Microsoft.Web/sites//functions/" + // Note: On some cloud providers, it may not be possible to determine the full + // ID at startup, + // so it may be necessary to set `cloud.resource_id` as a span attribute + // instead. + // + // The exact value to use for `cloud.resource_id` depends on the cloud provider. + // The following well-known definitions MUST be used if you set this attribute + // and they apply: + // + // - **AWS Lambda:** The function [ARN]. + // Take care not to use the "invoked ARN" directly but replace any + // [alias suffix] + // with the resolved function version, as the same runtime instance may be + // invocable with + // multiple different aliases. + // - **GCP:** The [URI of the resource] + // - **Azure:** The [Fully Qualified Resource ID] of the invoked function, + // *not* the function app, having the form + // + // `/subscriptions//resourceGroups//providers/Microsoft.Web/sites//functions/` + // . + // This means that a span attribute MUST be used, as an Azure function app + // can host multiple functions that would usually share + // a TracerProvider. + // + // + // [ARN]: https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html + // [fully qualified resource ID]: https://learn.microsoft.com/rest/api/resources/resources/get-by-id + // [full resource name]: https://google.aip.dev/122#full-resource-names + // [ARN]: https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html + // [alias suffix]: https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html + // [URI of the resource]: https://cloud.google.com/iam/docs/full-resource-names + // [Fully Qualified Resource ID]: https://learn.microsoft.com/rest/api/resources/resources/get-by-id + CloudResourceIDKey = attribute.Key("cloud.resource_id") +) + +// CloudAccountID returns an attribute KeyValue conforming to the +// "cloud.account.id" semantic conventions. It represents the cloud account ID +// the resource is assigned to. +func CloudAccountID(val string) attribute.KeyValue { + return CloudAccountIDKey.String(val) +} + +// CloudAvailabilityZone returns an attribute KeyValue conforming to the +// "cloud.availability_zone" semantic conventions. It represents the cloud +// regions often have multiple, isolated locations known as zones to increase +// availability. Availability zone represents the zone where the resource is +// running. +func CloudAvailabilityZone(val string) attribute.KeyValue { + return CloudAvailabilityZoneKey.String(val) +} + +// CloudRegion returns an attribute KeyValue conforming to the "cloud.region" +// semantic conventions. It represents the geographical region within a cloud +// provider. When associated with a resource, this attribute specifies the region +// where the resource operates. When calling services or APIs deployed on a +// cloud, this attribute identifies the region where the called destination is +// deployed. +func CloudRegion(val string) attribute.KeyValue { + return CloudRegionKey.String(val) +} + +// CloudResourceID returns an attribute KeyValue conforming to the +// "cloud.resource_id" semantic conventions. It represents the cloud +// provider-specific native identifier of the monitored cloud resource (e.g. an +// [ARN] on AWS, a [fully qualified resource ID] on Azure, a [full resource name] +// on GCP). +// +// [ARN]: https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html +// [fully qualified resource ID]: https://learn.microsoft.com/rest/api/resources/resources/get-by-id +// [full resource name]: https://google.aip.dev/122#full-resource-names +func CloudResourceID(val string) attribute.KeyValue { + return CloudResourceIDKey.String(val) +} + +// Enum values for cloud.platform +var ( + // Akamai Cloud Compute + // Stability: development + CloudPlatformAkamaiCloudCompute = CloudPlatformKey.String("akamai_cloud.compute") + // Alibaba Cloud Elastic Compute Service + // Stability: development + CloudPlatformAlibabaCloudECS = CloudPlatformKey.String("alibaba_cloud_ecs") + // Alibaba Cloud Function Compute + // Stability: development + CloudPlatformAlibabaCloudFC = CloudPlatformKey.String("alibaba_cloud_fc") + // Red Hat OpenShift on Alibaba Cloud + // Stability: development + CloudPlatformAlibabaCloudOpenShift = CloudPlatformKey.String("alibaba_cloud_openshift") + // AWS Elastic Compute Cloud + // Stability: development + CloudPlatformAWSEC2 = CloudPlatformKey.String("aws_ec2") + // AWS Elastic Container Service + // Stability: development + CloudPlatformAWSECS = CloudPlatformKey.String("aws_ecs") + // AWS Elastic Kubernetes Service + // Stability: development + CloudPlatformAWSEKS = CloudPlatformKey.String("aws_eks") + // AWS Lambda + // Stability: development + CloudPlatformAWSLambda = CloudPlatformKey.String("aws_lambda") + // AWS Elastic Beanstalk + // Stability: development + CloudPlatformAWSElasticBeanstalk = CloudPlatformKey.String("aws_elastic_beanstalk") + // AWS App Runner + // Stability: development + CloudPlatformAWSAppRunner = CloudPlatformKey.String("aws_app_runner") + // Red Hat OpenShift on AWS (ROSA) + // Stability: development + CloudPlatformAWSOpenShift = CloudPlatformKey.String("aws_openshift") + // Azure Virtual Machines + // Stability: development + CloudPlatformAzureVM = CloudPlatformKey.String("azure.vm") + // Azure Container Apps + // Stability: development + CloudPlatformAzureContainerApps = CloudPlatformKey.String("azure.container_apps") + // Azure Container Instances + // Stability: development + CloudPlatformAzureContainerInstances = CloudPlatformKey.String("azure.container_instances") + // Azure Kubernetes Service + // Stability: development + CloudPlatformAzureAKS = CloudPlatformKey.String("azure.aks") + // Azure Functions + // Stability: development + CloudPlatformAzureFunctions = CloudPlatformKey.String("azure.functions") + // Azure App Service + // Stability: development + CloudPlatformAzureAppService = CloudPlatformKey.String("azure.app_service") + // Azure Red Hat OpenShift + // Stability: development + CloudPlatformAzureOpenShift = CloudPlatformKey.String("azure.openshift") + // Google Vertex AI Agent Engine + // Stability: development + CloudPlatformGCPAgentEngine = CloudPlatformKey.String("gcp.agent_engine") + // Google Bare Metal Solution (BMS) + // Stability: development + CloudPlatformGCPBareMetalSolution = CloudPlatformKey.String("gcp_bare_metal_solution") + // Google Cloud Compute Engine (GCE) + // Stability: development + CloudPlatformGCPComputeEngine = CloudPlatformKey.String("gcp_compute_engine") + // Google Cloud Run + // Stability: development + CloudPlatformGCPCloudRun = CloudPlatformKey.String("gcp_cloud_run") + // Google Cloud Kubernetes Engine (GKE) + // Stability: development + CloudPlatformGCPKubernetesEngine = CloudPlatformKey.String("gcp_kubernetes_engine") + // Google Cloud Functions (GCF) + // Stability: development + CloudPlatformGCPCloudFunctions = CloudPlatformKey.String("gcp_cloud_functions") + // Google Cloud App Engine (GAE) + // Stability: development + CloudPlatformGCPAppEngine = CloudPlatformKey.String("gcp_app_engine") + // Red Hat OpenShift on Google Cloud + // Stability: development + CloudPlatformGCPOpenShift = CloudPlatformKey.String("gcp_openshift") + // Server on Hetzner Cloud + // Stability: development + CloudPlatformHetznerCloudServer = CloudPlatformKey.String("hetzner.cloud_server") + // Red Hat OpenShift on IBM Cloud + // Stability: development + CloudPlatformIBMCloudOpenShift = CloudPlatformKey.String("ibm_cloud_openshift") + // Compute on Oracle Cloud Infrastructure (OCI) + // Stability: development + CloudPlatformOracleCloudCompute = CloudPlatformKey.String("oracle_cloud_compute") + // Kubernetes Engine (OKE) on Oracle Cloud Infrastructure (OCI) + // Stability: development + CloudPlatformOracleCloudOKE = CloudPlatformKey.String("oracle_cloud_oke") + // Tencent Cloud Cloud Virtual Machine (CVM) + // Stability: development + CloudPlatformTencentCloudCVM = CloudPlatformKey.String("tencent_cloud_cvm") + // Tencent Cloud Elastic Kubernetes Service (EKS) + // Stability: development + CloudPlatformTencentCloudEKS = CloudPlatformKey.String("tencent_cloud_eks") + // Tencent Cloud Serverless Cloud Function (SCF) + // Stability: development + CloudPlatformTencentCloudSCF = CloudPlatformKey.String("tencent_cloud_scf") + // Vultr Cloud Compute + // Stability: development + CloudPlatformVultrCloudCompute = CloudPlatformKey.String("vultr.cloud_compute") +) + +// Enum values for cloud.provider +var ( + // Akamai Cloud + // Stability: development + CloudProviderAkamaiCloud = CloudProviderKey.String("akamai_cloud") + // Alibaba Cloud + // Stability: development + CloudProviderAlibabaCloud = CloudProviderKey.String("alibaba_cloud") + // Amazon Web Services + // Stability: development + CloudProviderAWS = CloudProviderKey.String("aws") + // Microsoft Azure + // Stability: development + CloudProviderAzure = CloudProviderKey.String("azure") + // Google Cloud Platform + // Stability: development + CloudProviderGCP = CloudProviderKey.String("gcp") + // Heroku Platform as a Service + // Stability: development + CloudProviderHeroku = CloudProviderKey.String("heroku") + // Hetzner + // Stability: development + CloudProviderHetzner = CloudProviderKey.String("hetzner") + // IBM Cloud + // Stability: development + CloudProviderIBMCloud = CloudProviderKey.String("ibm_cloud") + // Oracle Cloud Infrastructure (OCI) + // Stability: development + CloudProviderOracleCloud = CloudProviderKey.String("oracle_cloud") + // Tencent Cloud + // Stability: development + CloudProviderTencentCloud = CloudProviderKey.String("tencent_cloud") + // Vultr + // Stability: development + CloudProviderVultr = CloudProviderKey.String("vultr") +) + +// Namespace: cloudevents +const ( + // CloudEventsEventIDKey is the attribute Key conforming to the + // "cloudevents.event_id" semantic conventions. It represents the [event_id] + // uniquely identifies the event. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "123e4567-e89b-12d3-a456-426614174000", "0001" + // + // [event_id]: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#id + CloudEventsEventIDKey = attribute.Key("cloudevents.event_id") + + // CloudEventsEventSourceKey is the attribute Key conforming to the + // "cloudevents.event_source" semantic conventions. It represents the [source] + // identifies the context in which an event happened. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "https://github.com/cloudevents", "/cloudevents/spec/pull/123", + // "my-service" + // + // [source]: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#source-1 + CloudEventsEventSourceKey = attribute.Key("cloudevents.event_source") + + // CloudEventsEventSpecVersionKey is the attribute Key conforming to the + // "cloudevents.event_spec_version" semantic conventions. It represents the + // [version of the CloudEvents specification] which the event uses. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1.0 + // + // [version of the CloudEvents specification]: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#specversion + CloudEventsEventSpecVersionKey = attribute.Key("cloudevents.event_spec_version") + + // CloudEventsEventSubjectKey is the attribute Key conforming to the + // "cloudevents.event_subject" semantic conventions. It represents the [subject] + // of the event in the context of the event producer (identified by source). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: mynewfile.jpg + // + // [subject]: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#subject + CloudEventsEventSubjectKey = attribute.Key("cloudevents.event_subject") + + // CloudEventsEventTypeKey is the attribute Key conforming to the + // "cloudevents.event_type" semantic conventions. It represents the [event_type] + // contains a value describing the type of event related to the originating + // occurrence. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "com.github.pull_request.opened", "com.example.object.deleted.v2" + // + // [event_type]: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#type + CloudEventsEventTypeKey = attribute.Key("cloudevents.event_type") +) + +// CloudEventsEventID returns an attribute KeyValue conforming to the +// "cloudevents.event_id" semantic conventions. It represents the [event_id] +// uniquely identifies the event. +// +// [event_id]: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#id +func CloudEventsEventID(val string) attribute.KeyValue { + return CloudEventsEventIDKey.String(val) +} + +// CloudEventsEventSource returns an attribute KeyValue conforming to the +// "cloudevents.event_source" semantic conventions. It represents the [source] +// identifies the context in which an event happened. +// +// [source]: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#source-1 +func CloudEventsEventSource(val string) attribute.KeyValue { + return CloudEventsEventSourceKey.String(val) +} + +// CloudEventsEventSpecVersion returns an attribute KeyValue conforming to the +// "cloudevents.event_spec_version" semantic conventions. It represents the +// [version of the CloudEvents specification] which the event uses. +// +// [version of the CloudEvents specification]: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#specversion +func CloudEventsEventSpecVersion(val string) attribute.KeyValue { + return CloudEventsEventSpecVersionKey.String(val) +} + +// CloudEventsEventSubject returns an attribute KeyValue conforming to the +// "cloudevents.event_subject" semantic conventions. It represents the [subject] +// of the event in the context of the event producer (identified by source). +// +// [subject]: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#subject +func CloudEventsEventSubject(val string) attribute.KeyValue { + return CloudEventsEventSubjectKey.String(val) +} + +// CloudEventsEventType returns an attribute KeyValue conforming to the +// "cloudevents.event_type" semantic conventions. It represents the [event_type] +// contains a value describing the type of event related to the originating +// occurrence. +// +// [event_type]: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#type +func CloudEventsEventType(val string) attribute.KeyValue { + return CloudEventsEventTypeKey.String(val) +} + +// Namespace: cloudfoundry +const ( + // CloudFoundryAppIDKey is the attribute Key conforming to the + // "cloudfoundry.app.id" semantic conventions. It represents the guid of the + // application. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "218fc5a9-a5f1-4b54-aa05-46717d0ab26d" + // Note: Application instrumentation should use the value from environment + // variable `VCAP_APPLICATION.application_id`. This is the same value as + // reported by `cf app --guid`. + CloudFoundryAppIDKey = attribute.Key("cloudfoundry.app.id") + + // CloudFoundryAppInstanceIDKey is the attribute Key conforming to the + // "cloudfoundry.app.instance.id" semantic conventions. It represents the index + // of the application instance. 0 when just one instance is active. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "0", "1" + // Note: CloudFoundry defines the `instance_id` in the [Loggregator v2 envelope] + // . + // It is used for logs and metrics emitted by CloudFoundry. It is + // supposed to contain the application instance index for applications + // deployed on the runtime. + // + // Application instrumentation should use the value from environment + // variable `CF_INSTANCE_INDEX`. + // + // [Loggregator v2 envelope]: https://github.com/cloudfoundry/loggregator-api#v2-envelope + CloudFoundryAppInstanceIDKey = attribute.Key("cloudfoundry.app.instance.id") + + // CloudFoundryAppNameKey is the attribute Key conforming to the + // "cloudfoundry.app.name" semantic conventions. It represents the name of the + // application. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-app-name" + // Note: Application instrumentation should use the value from environment + // variable `VCAP_APPLICATION.application_name`. This is the same value + // as reported by `cf apps`. + CloudFoundryAppNameKey = attribute.Key("cloudfoundry.app.name") + + // CloudFoundryOrgIDKey is the attribute Key conforming to the + // "cloudfoundry.org.id" semantic conventions. It represents the guid of the + // CloudFoundry org the application is running in. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "218fc5a9-a5f1-4b54-aa05-46717d0ab26d" + // Note: Application instrumentation should use the value from environment + // variable `VCAP_APPLICATION.org_id`. This is the same value as + // reported by `cf org --guid`. + CloudFoundryOrgIDKey = attribute.Key("cloudfoundry.org.id") + + // CloudFoundryOrgNameKey is the attribute Key conforming to the + // "cloudfoundry.org.name" semantic conventions. It represents the name of the + // CloudFoundry organization the app is running in. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-org-name" + // Note: Application instrumentation should use the value from environment + // variable `VCAP_APPLICATION.org_name`. This is the same value as + // reported by `cf orgs`. + CloudFoundryOrgNameKey = attribute.Key("cloudfoundry.org.name") + + // CloudFoundryProcessIDKey is the attribute Key conforming to the + // "cloudfoundry.process.id" semantic conventions. It represents the UID + // identifying the process. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "218fc5a9-a5f1-4b54-aa05-46717d0ab26d" + // Note: Application instrumentation should use the value from environment + // variable `VCAP_APPLICATION.process_id`. It is supposed to be equal to + // `VCAP_APPLICATION.app_id` for applications deployed to the runtime. + // For system components, this could be the actual PID. + CloudFoundryProcessIDKey = attribute.Key("cloudfoundry.process.id") + + // CloudFoundryProcessTypeKey is the attribute Key conforming to the + // "cloudfoundry.process.type" semantic conventions. It represents the type of + // process. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "web" + // Note: CloudFoundry applications can consist of multiple jobs. Usually the + // main process will be of type `web`. There can be additional background + // tasks or side-cars with different process types. + CloudFoundryProcessTypeKey = attribute.Key("cloudfoundry.process.type") + + // CloudFoundrySpaceIDKey is the attribute Key conforming to the + // "cloudfoundry.space.id" semantic conventions. It represents the guid of the + // CloudFoundry space the application is running in. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "218fc5a9-a5f1-4b54-aa05-46717d0ab26d" + // Note: Application instrumentation should use the value from environment + // variable `VCAP_APPLICATION.space_id`. This is the same value as + // reported by `cf space --guid`. + CloudFoundrySpaceIDKey = attribute.Key("cloudfoundry.space.id") + + // CloudFoundrySpaceNameKey is the attribute Key conforming to the + // "cloudfoundry.space.name" semantic conventions. It represents the name of the + // CloudFoundry space the application is running in. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-space-name" + // Note: Application instrumentation should use the value from environment + // variable `VCAP_APPLICATION.space_name`. This is the same value as + // reported by `cf spaces`. + CloudFoundrySpaceNameKey = attribute.Key("cloudfoundry.space.name") + + // CloudFoundrySystemIDKey is the attribute Key conforming to the + // "cloudfoundry.system.id" semantic conventions. It represents a guid or + // another name describing the event source. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "cf/gorouter" + // Note: CloudFoundry defines the `source_id` in the [Loggregator v2 envelope]. + // It is used for logs and metrics emitted by CloudFoundry. It is + // supposed to contain the component name, e.g. "gorouter", for + // CloudFoundry components. + // + // When system components are instrumented, values from the + // [Bosh spec] + // should be used. The `system.id` should be set to + // `spec.deployment/spec.name`. + // + // [Loggregator v2 envelope]: https://github.com/cloudfoundry/loggregator-api#v2-envelope + // [Bosh spec]: https://bosh.io/docs/jobs/#properties-spec + CloudFoundrySystemIDKey = attribute.Key("cloudfoundry.system.id") + + // CloudFoundrySystemInstanceIDKey is the attribute Key conforming to the + // "cloudfoundry.system.instance.id" semantic conventions. It represents a guid + // describing the concrete instance of the event source. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "218fc5a9-a5f1-4b54-aa05-46717d0ab26d" + // Note: CloudFoundry defines the `instance_id` in the [Loggregator v2 envelope] + // . + // It is used for logs and metrics emitted by CloudFoundry. It is + // supposed to contain the vm id for CloudFoundry components. + // + // When system components are instrumented, values from the + // [Bosh spec] + // should be used. The `system.instance.id` should be set to `spec.id`. + // + // [Loggregator v2 envelope]: https://github.com/cloudfoundry/loggregator-api#v2-envelope + // [Bosh spec]: https://bosh.io/docs/jobs/#properties-spec + CloudFoundrySystemInstanceIDKey = attribute.Key("cloudfoundry.system.instance.id") +) + +// CloudFoundryAppID returns an attribute KeyValue conforming to the +// "cloudfoundry.app.id" semantic conventions. It represents the guid of the +// application. +func CloudFoundryAppID(val string) attribute.KeyValue { + return CloudFoundryAppIDKey.String(val) +} + +// CloudFoundryAppInstanceID returns an attribute KeyValue conforming to the +// "cloudfoundry.app.instance.id" semantic conventions. It represents the index +// of the application instance. 0 when just one instance is active. +func CloudFoundryAppInstanceID(val string) attribute.KeyValue { + return CloudFoundryAppInstanceIDKey.String(val) +} + +// CloudFoundryAppName returns an attribute KeyValue conforming to the +// "cloudfoundry.app.name" semantic conventions. It represents the name of the +// application. +func CloudFoundryAppName(val string) attribute.KeyValue { + return CloudFoundryAppNameKey.String(val) +} + +// CloudFoundryOrgID returns an attribute KeyValue conforming to the +// "cloudfoundry.org.id" semantic conventions. It represents the guid of the +// CloudFoundry org the application is running in. +func CloudFoundryOrgID(val string) attribute.KeyValue { + return CloudFoundryOrgIDKey.String(val) +} + +// CloudFoundryOrgName returns an attribute KeyValue conforming to the +// "cloudfoundry.org.name" semantic conventions. It represents the name of the +// CloudFoundry organization the app is running in. +func CloudFoundryOrgName(val string) attribute.KeyValue { + return CloudFoundryOrgNameKey.String(val) +} + +// CloudFoundryProcessID returns an attribute KeyValue conforming to the +// "cloudfoundry.process.id" semantic conventions. It represents the UID +// identifying the process. +func CloudFoundryProcessID(val string) attribute.KeyValue { + return CloudFoundryProcessIDKey.String(val) +} + +// CloudFoundryProcessType returns an attribute KeyValue conforming to the +// "cloudfoundry.process.type" semantic conventions. It represents the type of +// process. +func CloudFoundryProcessType(val string) attribute.KeyValue { + return CloudFoundryProcessTypeKey.String(val) +} + +// CloudFoundrySpaceID returns an attribute KeyValue conforming to the +// "cloudfoundry.space.id" semantic conventions. It represents the guid of the +// CloudFoundry space the application is running in. +func CloudFoundrySpaceID(val string) attribute.KeyValue { + return CloudFoundrySpaceIDKey.String(val) +} + +// CloudFoundrySpaceName returns an attribute KeyValue conforming to the +// "cloudfoundry.space.name" semantic conventions. It represents the name of the +// CloudFoundry space the application is running in. +func CloudFoundrySpaceName(val string) attribute.KeyValue { + return CloudFoundrySpaceNameKey.String(val) +} + +// CloudFoundrySystemID returns an attribute KeyValue conforming to the +// "cloudfoundry.system.id" semantic conventions. It represents a guid or another +// name describing the event source. +func CloudFoundrySystemID(val string) attribute.KeyValue { + return CloudFoundrySystemIDKey.String(val) +} + +// CloudFoundrySystemInstanceID returns an attribute KeyValue conforming to the +// "cloudfoundry.system.instance.id" semantic conventions. It represents a guid +// describing the concrete instance of the event source. +func CloudFoundrySystemInstanceID(val string) attribute.KeyValue { + return CloudFoundrySystemInstanceIDKey.String(val) +} + +// Namespace: code +const ( + // CodeColumnNumberKey is the attribute Key conforming to the + // "code.column.number" semantic conventions. It represents the column number in + // `code.file.path` best representing the operation. It SHOULD point within the + // code unit named in `code.function.name`. This attribute MUST NOT be used on + // the Profile signal since the data is already captured in 'message Line'. This + // constraint is imposed to prevent redundancy and maintain data integrity. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Stable + CodeColumnNumberKey = attribute.Key("code.column.number") + + // CodeFilePathKey is the attribute Key conforming to the "code.file.path" + // semantic conventions. It represents the source code file name that identifies + // the code unit as uniquely as possible (preferably an absolute file path). + // This attribute MUST NOT be used on the Profile signal since the data is + // already captured in 'message Function'. This constraint is imposed to prevent + // redundancy and maintain data integrity. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: /usr/local/MyApplication/content_root/app/index.php + CodeFilePathKey = attribute.Key("code.file.path") + + // CodeFunctionNameKey is the attribute Key conforming to the + // "code.function.name" semantic conventions. It represents the method or + // function fully-qualified name without arguments. The value should fit the + // natural representation of the language runtime, which is also likely the same + // used within `code.stacktrace` attribute value. This attribute MUST NOT be + // used on the Profile signal since the data is already captured in 'message + // Function'. This constraint is imposed to prevent redundancy and maintain data + // integrity. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "com.example.MyHttpService.serveRequest", + // "GuzzleHttp\Client::transfer", "fopen" + // Note: Values and format depends on each language runtime, thus it is + // impossible to provide an exhaustive list of examples. + // The values are usually the same (or prefixes of) the ones found in native + // stack trace representation stored in + // `code.stacktrace` without information on arguments. + // + // Examples: + // + // - Java method: `com.example.MyHttpService.serveRequest` + // - Java anonymous class method: `com.mycompany.Main$1.myMethod` + // - Java lambda method: + // `com.mycompany.Main$$Lambda/0x0000748ae4149c00.myMethod` + // - PHP function: `GuzzleHttp\Client::transfer` + // - Go function: `github.com/my/repo/pkg.foo.func5` + // - Elixir: `OpenTelemetry.Ctx.new` + // - Erlang: `opentelemetry_ctx:new` + // - Rust: `playground::my_module::my_cool_func` + // - C function: `fopen` + CodeFunctionNameKey = attribute.Key("code.function.name") + + // CodeLineNumberKey is the attribute Key conforming to the "code.line.number" + // semantic conventions. It represents the line number in `code.file.path` best + // representing the operation. It SHOULD point within the code unit named in + // `code.function.name`. This attribute MUST NOT be used on the Profile signal + // since the data is already captured in 'message Line'. This constraint is + // imposed to prevent redundancy and maintain data integrity. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Stable + CodeLineNumberKey = attribute.Key("code.line.number") + + // CodeStacktraceKey is the attribute Key conforming to the "code.stacktrace" + // semantic conventions. It represents a stacktrace as a string in the natural + // representation for the language runtime. The representation is identical to + // [`exception.stacktrace`]. This attribute MUST NOT be used on the Profile + // signal since the data is already captured in 'message Location'. This + // constraint is imposed to prevent redundancy and maintain data integrity. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: at com.example.GenerateTrace.methodB(GenerateTrace.java:13)\n at + // com.example.GenerateTrace.methodA(GenerateTrace.java:9)\n at + // com.example.GenerateTrace.main(GenerateTrace.java:5) + // + // [`exception.stacktrace`]: /docs/exceptions/exceptions-spans.md#stacktrace-representation + CodeStacktraceKey = attribute.Key("code.stacktrace") +) + +// CodeColumnNumber returns an attribute KeyValue conforming to the +// "code.column.number" semantic conventions. It represents the column number in +// `code.file.path` best representing the operation. It SHOULD point within the +// code unit named in `code.function.name`. This attribute MUST NOT be used on +// the Profile signal since the data is already captured in 'message Line'. This +// constraint is imposed to prevent redundancy and maintain data integrity. +func CodeColumnNumber(val int) attribute.KeyValue { + return CodeColumnNumberKey.Int(val) +} + +// CodeFilePath returns an attribute KeyValue conforming to the "code.file.path" +// semantic conventions. It represents the source code file name that identifies +// the code unit as uniquely as possible (preferably an absolute file path). This +// attribute MUST NOT be used on the Profile signal since the data is already +// captured in 'message Function'. This constraint is imposed to prevent +// redundancy and maintain data integrity. +func CodeFilePath(val string) attribute.KeyValue { + return CodeFilePathKey.String(val) +} + +// CodeFunctionName returns an attribute KeyValue conforming to the +// "code.function.name" semantic conventions. It represents the method or +// function fully-qualified name without arguments. The value should fit the +// natural representation of the language runtime, which is also likely the same +// used within `code.stacktrace` attribute value. This attribute MUST NOT be used +// on the Profile signal since the data is already captured in 'message +// Function'. This constraint is imposed to prevent redundancy and maintain data +// integrity. +func CodeFunctionName(val string) attribute.KeyValue { + return CodeFunctionNameKey.String(val) +} + +// CodeLineNumber returns an attribute KeyValue conforming to the +// "code.line.number" semantic conventions. It represents the line number in +// `code.file.path` best representing the operation. It SHOULD point within the +// code unit named in `code.function.name`. This attribute MUST NOT be used on +// the Profile signal since the data is already captured in 'message Line'. This +// constraint is imposed to prevent redundancy and maintain data integrity. +func CodeLineNumber(val int) attribute.KeyValue { + return CodeLineNumberKey.Int(val) +} + +// CodeStacktrace returns an attribute KeyValue conforming to the +// "code.stacktrace" semantic conventions. It represents a stacktrace as a string +// in the natural representation for the language runtime. The representation is +// identical to [`exception.stacktrace`]. This attribute MUST NOT be used on the +// Profile signal since the data is already captured in 'message Location'. This +// constraint is imposed to prevent redundancy and maintain data integrity. +// +// [`exception.stacktrace`]: /docs/exceptions/exceptions-spans.md#stacktrace-representation +func CodeStacktrace(val string) attribute.KeyValue { + return CodeStacktraceKey.String(val) +} + +// Namespace: container +const ( + // ContainerCommandKey is the attribute Key conforming to the + // "container.command" semantic conventions. It represents the command used to + // run the container (i.e. the command name). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "otelcontribcol" + // Note: If using embedded credentials or sensitive data, it is recommended to + // remove them to prevent potential leakage. + ContainerCommandKey = attribute.Key("container.command") + + // ContainerCommandArgsKey is the attribute Key conforming to the + // "container.command_args" semantic conventions. It represents the all the + // command arguments (including the command/executable itself) run by the + // container. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "otelcontribcol", "--config", "config.yaml" + ContainerCommandArgsKey = attribute.Key("container.command_args") + + // ContainerCommandLineKey is the attribute Key conforming to the + // "container.command_line" semantic conventions. It represents the full command + // run by the container as a single string representing the full command. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "otelcontribcol --config config.yaml" + ContainerCommandLineKey = attribute.Key("container.command_line") + + // ContainerCSIPluginNameKey is the attribute Key conforming to the + // "container.csi.plugin.name" semantic conventions. It represents the name of + // the CSI ([Container Storage Interface]) plugin used by the volume. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "pd.csi.storage.gke.io" + // Note: This can sometimes be referred to as a "driver" in CSI implementations. + // This should represent the `name` field of the GetPluginInfo RPC. + // + // [Container Storage Interface]: https://github.com/container-storage-interface/spec + ContainerCSIPluginNameKey = attribute.Key("container.csi.plugin.name") + + // ContainerCSIVolumeIDKey is the attribute Key conforming to the + // "container.csi.volume.id" semantic conventions. It represents the unique + // volume ID returned by the CSI ([Container Storage Interface]) plugin. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "projects/my-gcp-project/zones/my-gcp-zone/disks/my-gcp-disk" + // Note: This can sometimes be referred to as a "volume handle" in CSI + // implementations. This should represent the `Volume.volume_id` field in CSI + // spec. + // + // [Container Storage Interface]: https://github.com/container-storage-interface/spec + ContainerCSIVolumeIDKey = attribute.Key("container.csi.volume.id") + + // ContainerIDKey is the attribute Key conforming to the "container.id" semantic + // conventions. It represents the container ID. Usually a UUID, as for example + // used to [identify Docker containers]. The UUID might be abbreviated. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "a3bf90e006b2" + // + // [identify Docker containers]: https://docs.docker.com/engine/containers/run/#container-identification + ContainerIDKey = attribute.Key("container.id") + + // ContainerImageIDKey is the attribute Key conforming to the + // "container.image.id" semantic conventions. It represents the runtime specific + // image identifier. Usually a hash algorithm followed by a UUID. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "sha256:19c92d0a00d1b66d897bceaa7319bee0dd38a10a851c60bcec9474aa3f01e50f" + // Note: Docker defines a sha256 of the image id; `container.image.id` + // corresponds to the `Image` field from the Docker container inspect [API] + // endpoint. + // K8s defines a link to the container registry repository with digest + // `"imageID": "registry.azurecr.io /namespace/service/dockerfile@sha256:bdeabd40c3a8a492eaf9e8e44d0ebbb84bac7ee25ac0cf8a7159d25f62555625"` + // . + // The ID is assigned by the container runtime and can vary in different + // environments. Consider using `oci.manifest.digest` if it is important to + // identify the same image in different environments/runtimes. + // + // [API]: https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Container/operation/ContainerInspect + ContainerImageIDKey = attribute.Key("container.image.id") + + // ContainerImageNameKey is the attribute Key conforming to the + // "container.image.name" semantic conventions. It represents the name of the + // image the container was built on. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "gcr.io/opentelemetry/operator" + ContainerImageNameKey = attribute.Key("container.image.name") + + // ContainerImageRepoDigestsKey is the attribute Key conforming to the + // "container.image.repo_digests" semantic conventions. It represents the repo + // digests of the container image as provided by the container runtime. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: + // "example@sha256:afcc7f1ac1b49db317a7196c902e61c6c3c4607d63599ee1a82d702d249a0ccb", + // "internal.registry.example.com:5000/example@sha256:b69959407d21e8a062e0416bf13405bb2b71ed7a84dde4158ebafacfa06f5578" + // Note: [Docker] and [CRI] report those under the `RepoDigests` field. + // + // [Docker]: https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Image/operation/ImageInspect + // [CRI]: https://github.com/kubernetes/cri-api/blob/c75ef5b473bbe2d0a4fc92f82235efd665ea8e9f/pkg/apis/runtime/v1/api.proto#L1237-L1238 + ContainerImageRepoDigestsKey = attribute.Key("container.image.repo_digests") + + // ContainerImageTagsKey is the attribute Key conforming to the + // "container.image.tags" semantic conventions. It represents the container + // image tags. An example can be found in [Docker Image Inspect]. Should be only + // the `` section of the full name for example from + // `registry.example.com/my-org/my-image:`. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "v1.27.1", "3.5.7-0" + // + // [Docker Image Inspect]: https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Image/operation/ImageInspect + ContainerImageTagsKey = attribute.Key("container.image.tags") + + // ContainerNameKey is the attribute Key conforming to the "container.name" + // semantic conventions. It represents the container name used by container + // runtime. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "opentelemetry-autoconf" + ContainerNameKey = attribute.Key("container.name") + + // ContainerRuntimeDescriptionKey is the attribute Key conforming to the + // "container.runtime.description" semantic conventions. It represents a + // description about the runtime which could include, for example details about + // the CRI/API version being used or other customisations. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "docker://19.3.1 - CRI: 1.22.0" + ContainerRuntimeDescriptionKey = attribute.Key("container.runtime.description") + + // ContainerRuntimeNameKey is the attribute Key conforming to the + // "container.runtime.name" semantic conventions. It represents the container + // runtime managing this container. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "docker", "containerd", "rkt" + ContainerRuntimeNameKey = attribute.Key("container.runtime.name") + + // ContainerRuntimeVersionKey is the attribute Key conforming to the + // "container.runtime.version" semantic conventions. It represents the version + // of the runtime of this process, as returned by the runtime without + // modification. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1.0.0 + ContainerRuntimeVersionKey = attribute.Key("container.runtime.version") +) + +// ContainerCommand returns an attribute KeyValue conforming to the +// "container.command" semantic conventions. It represents the command used to +// run the container (i.e. the command name). +func ContainerCommand(val string) attribute.KeyValue { + return ContainerCommandKey.String(val) +} + +// ContainerCommandArgs returns an attribute KeyValue conforming to the +// "container.command_args" semantic conventions. It represents the all the +// command arguments (including the command/executable itself) run by the +// container. +func ContainerCommandArgs(val ...string) attribute.KeyValue { + return ContainerCommandArgsKey.StringSlice(val) +} + +// ContainerCommandLine returns an attribute KeyValue conforming to the +// "container.command_line" semantic conventions. It represents the full command +// run by the container as a single string representing the full command. +func ContainerCommandLine(val string) attribute.KeyValue { + return ContainerCommandLineKey.String(val) +} + +// ContainerCSIPluginName returns an attribute KeyValue conforming to the +// "container.csi.plugin.name" semantic conventions. It represents the name of +// the CSI ([Container Storage Interface]) plugin used by the volume. +// +// [Container Storage Interface]: https://github.com/container-storage-interface/spec +func ContainerCSIPluginName(val string) attribute.KeyValue { + return ContainerCSIPluginNameKey.String(val) +} + +// ContainerCSIVolumeID returns an attribute KeyValue conforming to the +// "container.csi.volume.id" semantic conventions. It represents the unique +// volume ID returned by the CSI ([Container Storage Interface]) plugin. +// +// [Container Storage Interface]: https://github.com/container-storage-interface/spec +func ContainerCSIVolumeID(val string) attribute.KeyValue { + return ContainerCSIVolumeIDKey.String(val) +} + +// ContainerID returns an attribute KeyValue conforming to the "container.id" +// semantic conventions. It represents the container ID. Usually a UUID, as for +// example used to [identify Docker containers]. The UUID might be abbreviated. +// +// [identify Docker containers]: https://docs.docker.com/engine/containers/run/#container-identification +func ContainerID(val string) attribute.KeyValue { + return ContainerIDKey.String(val) +} + +// ContainerImageID returns an attribute KeyValue conforming to the +// "container.image.id" semantic conventions. It represents the runtime specific +// image identifier. Usually a hash algorithm followed by a UUID. +func ContainerImageID(val string) attribute.KeyValue { + return ContainerImageIDKey.String(val) +} + +// ContainerImageName returns an attribute KeyValue conforming to the +// "container.image.name" semantic conventions. It represents the name of the +// image the container was built on. +func ContainerImageName(val string) attribute.KeyValue { + return ContainerImageNameKey.String(val) +} + +// ContainerImageRepoDigests returns an attribute KeyValue conforming to the +// "container.image.repo_digests" semantic conventions. It represents the repo +// digests of the container image as provided by the container runtime. +func ContainerImageRepoDigests(val ...string) attribute.KeyValue { + return ContainerImageRepoDigestsKey.StringSlice(val) +} + +// ContainerImageTags returns an attribute KeyValue conforming to the +// "container.image.tags" semantic conventions. It represents the container image +// tags. An example can be found in [Docker Image Inspect]. Should be only the +// `` section of the full name for example from +// `registry.example.com/my-org/my-image:`. +// +// [Docker Image Inspect]: https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Image/operation/ImageInspect +func ContainerImageTags(val ...string) attribute.KeyValue { + return ContainerImageTagsKey.StringSlice(val) +} + +// ContainerLabel returns an attribute KeyValue conforming to the +// "container.label" semantic conventions. It represents the container labels, +// `` being the label name, the value being the label value. +func ContainerLabel(key string, val string) attribute.KeyValue { + return attribute.String("container.label."+key, val) +} + +// ContainerName returns an attribute KeyValue conforming to the "container.name" +// semantic conventions. It represents the container name used by container +// runtime. +func ContainerName(val string) attribute.KeyValue { + return ContainerNameKey.String(val) +} + +// ContainerRuntimeDescription returns an attribute KeyValue conforming to the +// "container.runtime.description" semantic conventions. It represents a +// description about the runtime which could include, for example details about +// the CRI/API version being used or other customisations. +func ContainerRuntimeDescription(val string) attribute.KeyValue { + return ContainerRuntimeDescriptionKey.String(val) +} + +// ContainerRuntimeName returns an attribute KeyValue conforming to the +// "container.runtime.name" semantic conventions. It represents the container +// runtime managing this container. +func ContainerRuntimeName(val string) attribute.KeyValue { + return ContainerRuntimeNameKey.String(val) +} + +// ContainerRuntimeVersion returns an attribute KeyValue conforming to the +// "container.runtime.version" semantic conventions. It represents the version of +// the runtime of this process, as returned by the runtime without modification. +func ContainerRuntimeVersion(val string) attribute.KeyValue { + return ContainerRuntimeVersionKey.String(val) +} + +// Namespace: cpu +const ( + // CPULogicalNumberKey is the attribute Key conforming to the + // "cpu.logical_number" semantic conventions. It represents the logical CPU + // number [0..n-1]. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1 + CPULogicalNumberKey = attribute.Key("cpu.logical_number") + + // CPUModeKey is the attribute Key conforming to the "cpu.mode" semantic + // conventions. It represents the mode of the CPU. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "user", "system" + CPUModeKey = attribute.Key("cpu.mode") +) + +// CPULogicalNumber returns an attribute KeyValue conforming to the +// "cpu.logical_number" semantic conventions. It represents the logical CPU +// number [0..n-1]. +func CPULogicalNumber(val int) attribute.KeyValue { + return CPULogicalNumberKey.Int(val) +} + +// Enum values for cpu.mode +var ( + // User + // Stability: development + CPUModeUser = CPUModeKey.String("user") + // System + // Stability: development + CPUModeSystem = CPUModeKey.String("system") + // Nice + // Stability: development + CPUModeNice = CPUModeKey.String("nice") + // Idle + // Stability: development + CPUModeIdle = CPUModeKey.String("idle") + // IO Wait + // Stability: development + CPUModeIOWait = CPUModeKey.String("iowait") + // Interrupt + // Stability: development + CPUModeInterrupt = CPUModeKey.String("interrupt") + // Steal + // Stability: development + CPUModeSteal = CPUModeKey.String("steal") + // Kernel + // Stability: development + CPUModeKernel = CPUModeKey.String("kernel") +) + +// Namespace: db +const ( + // DBClientConnectionPoolNameKey is the attribute Key conforming to the + // "db.client.connection.pool.name" semantic conventions. It represents the name + // of the connection pool; unique within the instrumented application. In case + // the connection pool implementation doesn't provide a name, instrumentation + // SHOULD use a combination of parameters that would make the name unique, for + // example, combining attributes `server.address`, `server.port`, and + // `db.namespace`, formatted as `server.address:server.port/db.namespace`. + // Instrumentations that generate connection pool name following different + // patterns SHOULD document it. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "myDataSource" + DBClientConnectionPoolNameKey = attribute.Key("db.client.connection.pool.name") + + // DBClientConnectionStateKey is the attribute Key conforming to the + // "db.client.connection.state" semantic conventions. It represents the state of + // a connection in the pool. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "idle" + DBClientConnectionStateKey = attribute.Key("db.client.connection.state") + + // DBCollectionNameKey is the attribute Key conforming to the + // "db.collection.name" semantic conventions. It represents the name of a + // collection (table, container) within the database. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "public.users", "customers" + // Note: It is RECOMMENDED to capture the value as provided by the application + // without attempting to do any case normalization. + // + // The collection name SHOULD NOT be extracted from `db.query.text`, + // when the database system supports query text with multiple collections + // in non-batch operations. + // + // For batch operations, if the individual operations are known to have the same + // collection name then that collection name SHOULD be used. + DBCollectionNameKey = attribute.Key("db.collection.name") + + // DBNamespaceKey is the attribute Key conforming to the "db.namespace" semantic + // conventions. It represents the name of the database, fully qualified within + // the server address and port. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "customers", "test.users" + // Note: If a database system has multiple namespace components, they SHOULD be + // concatenated from the most general to the most specific namespace component, + // using `|` as a separator between the components. Any missing components (and + // their associated separators) SHOULD be omitted. + // Semantic conventions for individual database systems SHOULD document what + // `db.namespace` means in the context of that system. + // It is RECOMMENDED to capture the value as provided by the application without + // attempting to do any case normalization. + DBNamespaceKey = attribute.Key("db.namespace") + + // DBOperationBatchSizeKey is the attribute Key conforming to the + // "db.operation.batch.size" semantic conventions. It represents the number of + // queries included in a batch operation. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: 2, 3, 4 + // Note: Operations are only considered batches when they contain two or more + // operations, and so `db.operation.batch.size` SHOULD never be `1`. + DBOperationBatchSizeKey = attribute.Key("db.operation.batch.size") + + // DBOperationNameKey is the attribute Key conforming to the "db.operation.name" + // semantic conventions. It represents the name of the operation or command + // being executed. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "findAndModify", "HMSET", "SELECT" + // Note: It is RECOMMENDED to capture the value as provided by the application + // without attempting to do any case normalization. + // + // The operation name SHOULD NOT be extracted from `db.query.text`, + // when the database system supports query text with multiple operations + // in non-batch operations. + // + // If spaces can occur in the operation name, multiple consecutive spaces + // SHOULD be normalized to a single space. + // + // For batch operations, if the individual operations are known to have the same + // operation name + // then that operation name SHOULD be used prepended by `BATCH `, + // otherwise `db.operation.name` SHOULD be `BATCH` or some other database + // system specific term if more applicable. + DBOperationNameKey = attribute.Key("db.operation.name") + + // DBQuerySummaryKey is the attribute Key conforming to the "db.query.summary" + // semantic conventions. It represents the low cardinality summary of a database + // query. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "SELECT wuser_table", "INSERT shipping_details SELECT orders", "get + // user by id" + // Note: The query summary describes a class of database queries and is useful + // as a grouping key, especially when analyzing telemetry for database + // calls involving complex queries. + // + // Summary may be available to the instrumentation through + // instrumentation hooks or other means. If it is not available, + // instrumentations + // that support query parsing SHOULD generate a summary following + // [Generating query summary] + // section. + // + // [Generating query summary]: /docs/db/database-spans.md#generating-a-summary-of-the-query + DBQuerySummaryKey = attribute.Key("db.query.summary") + + // DBQueryTextKey is the attribute Key conforming to the "db.query.text" + // semantic conventions. It represents the database query being executed. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "SELECT * FROM wuser_table where username = ?", "SET mykey ?" + // Note: For sanitization see [Sanitization of `db.query.text`]. + // For batch operations, if the individual operations are known to have the same + // query text then that query text SHOULD be used, otherwise all of the + // individual query texts SHOULD be concatenated with separator `; ` or some + // other database system specific separator if more applicable. + // Parameterized query text SHOULD NOT be sanitized. Even though parameterized + // query text can potentially have sensitive data, by using a parameterized + // query the user is giving a strong signal that any sensitive data will be + // passed as parameter values, and the benefit to observability of capturing the + // static part of the query text by default outweighs the risk. + // + // [Sanitization of `db.query.text`]: /docs/db/database-spans.md#sanitization-of-dbquerytext + DBQueryTextKey = attribute.Key("db.query.text") + + // DBResponseReturnedRowsKey is the attribute Key conforming to the + // "db.response.returned_rows" semantic conventions. It represents the number of + // rows returned by the operation. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 10, 30, 1000 + DBResponseReturnedRowsKey = attribute.Key("db.response.returned_rows") + + // DBResponseStatusCodeKey is the attribute Key conforming to the + // "db.response.status_code" semantic conventions. It represents the database + // response status code. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "102", "ORA-17002", "08P01", "404" + // Note: The status code returned by the database. Usually it represents an + // error code, but may also represent partial success, warning, or differentiate + // between various types of successful outcomes. + // Semantic conventions for individual database systems SHOULD document what + // `db.response.status_code` means in the context of that system. + DBResponseStatusCodeKey = attribute.Key("db.response.status_code") + + // DBStoredProcedureNameKey is the attribute Key conforming to the + // "db.stored_procedure.name" semantic conventions. It represents the name of a + // stored procedure within the database. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "GetCustomer" + // Note: It is RECOMMENDED to capture the value as provided by the application + // without attempting to do any case normalization. + // + // For batch operations, if the individual operations are known to have the same + // stored procedure name then that stored procedure name SHOULD be used. + DBStoredProcedureNameKey = attribute.Key("db.stored_procedure.name") + + // DBSystemNameKey is the attribute Key conforming to the "db.system.name" + // semantic conventions. It represents the database management system (DBMS) + // product as identified by the client instrumentation. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: + // Note: The actual DBMS may differ from the one identified by the client. For + // example, when using PostgreSQL client libraries to connect to a CockroachDB, + // the `db.system.name` is set to `postgresql` based on the instrumentation's + // best knowledge. + DBSystemNameKey = attribute.Key("db.system.name") +) + +// DBClientConnectionPoolName returns an attribute KeyValue conforming to the +// "db.client.connection.pool.name" semantic conventions. It represents the name +// of the connection pool; unique within the instrumented application. In case +// the connection pool implementation doesn't provide a name, instrumentation +// SHOULD use a combination of parameters that would make the name unique, for +// example, combining attributes `server.address`, `server.port`, and +// `db.namespace`, formatted as `server.address:server.port/db.namespace`. +// Instrumentations that generate connection pool name following different +// patterns SHOULD document it. +func DBClientConnectionPoolName(val string) attribute.KeyValue { + return DBClientConnectionPoolNameKey.String(val) +} + +// DBCollectionName returns an attribute KeyValue conforming to the +// "db.collection.name" semantic conventions. It represents the name of a +// collection (table, container) within the database. +func DBCollectionName(val string) attribute.KeyValue { + return DBCollectionNameKey.String(val) +} + +// DBNamespace returns an attribute KeyValue conforming to the "db.namespace" +// semantic conventions. It represents the name of the database, fully qualified +// within the server address and port. +func DBNamespace(val string) attribute.KeyValue { + return DBNamespaceKey.String(val) +} + +// DBOperationBatchSize returns an attribute KeyValue conforming to the +// "db.operation.batch.size" semantic conventions. It represents the number of +// queries included in a batch operation. +func DBOperationBatchSize(val int) attribute.KeyValue { + return DBOperationBatchSizeKey.Int(val) +} + +// DBOperationName returns an attribute KeyValue conforming to the +// "db.operation.name" semantic conventions. It represents the name of the +// operation or command being executed. +func DBOperationName(val string) attribute.KeyValue { + return DBOperationNameKey.String(val) +} + +// DBOperationParameter returns an attribute KeyValue conforming to the +// "db.operation.parameter" semantic conventions. It represents a database +// operation parameter, with `` being the parameter name, and the attribute +// value being a string representation of the parameter value. +func DBOperationParameter(key string, val string) attribute.KeyValue { + return attribute.String("db.operation.parameter."+key, val) +} + +// DBQueryParameter returns an attribute KeyValue conforming to the +// "db.query.parameter" semantic conventions. It represents a database query +// parameter, with `` being the parameter name, and the attribute value +// being a string representation of the parameter value. +func DBQueryParameter(key string, val string) attribute.KeyValue { + return attribute.String("db.query.parameter."+key, val) +} + +// DBQuerySummary returns an attribute KeyValue conforming to the +// "db.query.summary" semantic conventions. It represents the low cardinality +// summary of a database query. +func DBQuerySummary(val string) attribute.KeyValue { + return DBQuerySummaryKey.String(val) +} + +// DBQueryText returns an attribute KeyValue conforming to the "db.query.text" +// semantic conventions. It represents the database query being executed. +func DBQueryText(val string) attribute.KeyValue { + return DBQueryTextKey.String(val) +} + +// DBResponseReturnedRows returns an attribute KeyValue conforming to the +// "db.response.returned_rows" semantic conventions. It represents the number of +// rows returned by the operation. +func DBResponseReturnedRows(val int) attribute.KeyValue { + return DBResponseReturnedRowsKey.Int(val) +} + +// DBResponseStatusCode returns an attribute KeyValue conforming to the +// "db.response.status_code" semantic conventions. It represents the database +// response status code. +func DBResponseStatusCode(val string) attribute.KeyValue { + return DBResponseStatusCodeKey.String(val) +} + +// DBStoredProcedureName returns an attribute KeyValue conforming to the +// "db.stored_procedure.name" semantic conventions. It represents the name of a +// stored procedure within the database. +func DBStoredProcedureName(val string) attribute.KeyValue { + return DBStoredProcedureNameKey.String(val) +} + +// Enum values for db.client.connection.state +var ( + // idle + // Stability: development + DBClientConnectionStateIdle = DBClientConnectionStateKey.String("idle") + // used + // Stability: development + DBClientConnectionStateUsed = DBClientConnectionStateKey.String("used") +) + +// Enum values for db.system.name +var ( + // Some other SQL database. Fallback only. + // Stability: development + DBSystemNameOtherSQL = DBSystemNameKey.String("other_sql") + // [Adabas (Adaptable Database System)] + // Stability: development + // + // [Adabas (Adaptable Database System)]: https://documentation.softwareag.com/?pf=adabas + DBSystemNameSoftwareagAdabas = DBSystemNameKey.String("softwareag.adabas") + // [Actian Ingres] + // Stability: development + // + // [Actian Ingres]: https://www.actian.com/databases/ingres/ + DBSystemNameActianIngres = DBSystemNameKey.String("actian.ingres") + // [Amazon DynamoDB] + // Stability: development + // + // [Amazon DynamoDB]: https://aws.amazon.com/pm/dynamodb/ + DBSystemNameAWSDynamoDB = DBSystemNameKey.String("aws.dynamodb") + // [Amazon Redshift] + // Stability: development + // + // [Amazon Redshift]: https://aws.amazon.com/redshift/ + DBSystemNameAWSRedshift = DBSystemNameKey.String("aws.redshift") + // [Azure Cosmos DB] + // Stability: development + // + // [Azure Cosmos DB]: https://learn.microsoft.com/azure/cosmos-db + DBSystemNameAzureCosmosDB = DBSystemNameKey.String("azure.cosmosdb") + // [InterSystems Caché] + // Stability: development + // + // [InterSystems Caché]: https://www.intersystems.com/products/cache/ + DBSystemNameIntersystemsCache = DBSystemNameKey.String("intersystems.cache") + // [Apache Cassandra] + // Stability: development + // + // [Apache Cassandra]: https://cassandra.apache.org/ + DBSystemNameCassandra = DBSystemNameKey.String("cassandra") + // [ClickHouse] + // Stability: development + // + // [ClickHouse]: https://clickhouse.com/ + DBSystemNameClickHouse = DBSystemNameKey.String("clickhouse") + // [CockroachDB] + // Stability: development + // + // [CockroachDB]: https://www.cockroachlabs.com/ + DBSystemNameCockroachDB = DBSystemNameKey.String("cockroachdb") + // [Couchbase] + // Stability: development + // + // [Couchbase]: https://www.couchbase.com/ + DBSystemNameCouchbase = DBSystemNameKey.String("couchbase") + // [Apache CouchDB] + // Stability: development + // + // [Apache CouchDB]: https://couchdb.apache.org/ + DBSystemNameCouchDB = DBSystemNameKey.String("couchdb") + // [Apache Derby] + // Stability: development + // + // [Apache Derby]: https://db.apache.org/derby/ + DBSystemNameDerby = DBSystemNameKey.String("derby") + // [Elasticsearch] + // Stability: development + // + // [Elasticsearch]: https://www.elastic.co/elasticsearch + DBSystemNameElasticsearch = DBSystemNameKey.String("elasticsearch") + // [Firebird] + // Stability: development + // + // [Firebird]: https://www.firebirdsql.org/ + DBSystemNameFirebirdSQL = DBSystemNameKey.String("firebirdsql") + // [Google Cloud Spanner] + // Stability: development + // + // [Google Cloud Spanner]: https://cloud.google.com/spanner + DBSystemNameGCPSpanner = DBSystemNameKey.String("gcp.spanner") + // [Apache Geode] + // Stability: development + // + // [Apache Geode]: https://geode.apache.org/ + DBSystemNameGeode = DBSystemNameKey.String("geode") + // [H2 Database] + // Stability: development + // + // [H2 Database]: https://h2database.com/ + DBSystemNameH2database = DBSystemNameKey.String("h2database") + // [Apache HBase] + // Stability: development + // + // [Apache HBase]: https://hbase.apache.org/ + DBSystemNameHBase = DBSystemNameKey.String("hbase") + // [Apache Hive] + // Stability: development + // + // [Apache Hive]: https://hive.apache.org/ + DBSystemNameHive = DBSystemNameKey.String("hive") + // [HyperSQL Database] + // Stability: development + // + // [HyperSQL Database]: https://hsqldb.org/ + DBSystemNameHSQLDB = DBSystemNameKey.String("hsqldb") + // [IBM Db2] + // Stability: development + // + // [IBM Db2]: https://www.ibm.com/db2 + DBSystemNameIBMDB2 = DBSystemNameKey.String("ibm.db2") + // [IBM Informix] + // Stability: development + // + // [IBM Informix]: https://www.ibm.com/products/informix + DBSystemNameIBMInformix = DBSystemNameKey.String("ibm.informix") + // [IBM Netezza] + // Stability: development + // + // [IBM Netezza]: https://www.ibm.com/products/netezza + DBSystemNameIBMNetezza = DBSystemNameKey.String("ibm.netezza") + // [InfluxDB] + // Stability: development + // + // [InfluxDB]: https://www.influxdata.com/ + DBSystemNameInfluxDB = DBSystemNameKey.String("influxdb") + // [Instant] + // Stability: development + // + // [Instant]: https://www.instantdb.com/ + DBSystemNameInstantDB = DBSystemNameKey.String("instantdb") + // [MariaDB] + // Stability: stable + // + // [MariaDB]: https://mariadb.org/ + DBSystemNameMariaDB = DBSystemNameKey.String("mariadb") + // [Memcached] + // Stability: development + // + // [Memcached]: https://memcached.org/ + DBSystemNameMemcached = DBSystemNameKey.String("memcached") + // [MongoDB] + // Stability: development + // + // [MongoDB]: https://www.mongodb.com/ + DBSystemNameMongoDB = DBSystemNameKey.String("mongodb") + // [Microsoft SQL Server] + // Stability: stable + // + // [Microsoft SQL Server]: https://www.microsoft.com/sql-server + DBSystemNameMicrosoftSQLServer = DBSystemNameKey.String("microsoft.sql_server") + // [MySQL] + // Stability: stable + // + // [MySQL]: https://www.mysql.com/ + DBSystemNameMySQL = DBSystemNameKey.String("mysql") + // [Neo4j] + // Stability: development + // + // [Neo4j]: https://neo4j.com/ + DBSystemNameNeo4j = DBSystemNameKey.String("neo4j") + // [OpenSearch] + // Stability: development + // + // [OpenSearch]: https://opensearch.org/ + DBSystemNameOpenSearch = DBSystemNameKey.String("opensearch") + // [Oracle Database] + // Stability: development + // + // [Oracle Database]: https://www.oracle.com/database/ + DBSystemNameOracleDB = DBSystemNameKey.String("oracle.db") + // [PostgreSQL] + // Stability: stable + // + // [PostgreSQL]: https://www.postgresql.org/ + DBSystemNamePostgreSQL = DBSystemNameKey.String("postgresql") + // [Redis] + // Stability: development + // + // [Redis]: https://redis.io/ + DBSystemNameRedis = DBSystemNameKey.String("redis") + // [SAP HANA] + // Stability: development + // + // [SAP HANA]: https://www.sap.com/products/technology-platform/hana/what-is-sap-hana.html + DBSystemNameSAPHANA = DBSystemNameKey.String("sap.hana") + // [SAP MaxDB] + // Stability: development + // + // [SAP MaxDB]: https://maxdb.sap.com/ + DBSystemNameSAPMaxDB = DBSystemNameKey.String("sap.maxdb") + // [SQLite] + // Stability: development + // + // [SQLite]: https://www.sqlite.org/ + DBSystemNameSQLite = DBSystemNameKey.String("sqlite") + // [Teradata] + // Stability: development + // + // [Teradata]: https://www.teradata.com/ + DBSystemNameTeradata = DBSystemNameKey.String("teradata") + // [Trino] + // Stability: development + // + // [Trino]: https://trino.io/ + DBSystemNameTrino = DBSystemNameKey.String("trino") +) + +// Namespace: deployment +const ( + // DeploymentEnvironmentNameKey is the attribute Key conforming to the + // "deployment.environment.name" semantic conventions. It represents the name of + // the [deployment environment] (aka deployment tier). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "staging", "production" + // Note: `deployment.environment.name` does not affect the uniqueness + // constraints defined through + // the `service.namespace`, `service.name` and `service.instance.id` resource + // attributes. + // This implies that resources carrying the following attribute combinations + // MUST be + // considered to be identifying the same service: + // + // - `service.name=frontend`, `deployment.environment.name=production` + // - `service.name=frontend`, `deployment.environment.name=staging`. + // + // + // [deployment environment]: https://wikipedia.org/wiki/Deployment_environment + DeploymentEnvironmentNameKey = attribute.Key("deployment.environment.name") + + // DeploymentIDKey is the attribute Key conforming to the "deployment.id" + // semantic conventions. It represents the id of the deployment. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "1208" + DeploymentIDKey = attribute.Key("deployment.id") + + // DeploymentNameKey is the attribute Key conforming to the "deployment.name" + // semantic conventions. It represents the name of the deployment. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "deploy my app", "deploy-frontend" + DeploymentNameKey = attribute.Key("deployment.name") + + // DeploymentStatusKey is the attribute Key conforming to the + // "deployment.status" semantic conventions. It represents the status of the + // deployment. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + DeploymentStatusKey = attribute.Key("deployment.status") +) + +// DeploymentEnvironmentName returns an attribute KeyValue conforming to the +// "deployment.environment.name" semantic conventions. It represents the name of +// the [deployment environment] (aka deployment tier). +// +// [deployment environment]: https://wikipedia.org/wiki/Deployment_environment +func DeploymentEnvironmentName(val string) attribute.KeyValue { + return DeploymentEnvironmentNameKey.String(val) +} + +// DeploymentID returns an attribute KeyValue conforming to the "deployment.id" +// semantic conventions. It represents the id of the deployment. +func DeploymentID(val string) attribute.KeyValue { + return DeploymentIDKey.String(val) +} + +// DeploymentName returns an attribute KeyValue conforming to the +// "deployment.name" semantic conventions. It represents the name of the +// deployment. +func DeploymentName(val string) attribute.KeyValue { + return DeploymentNameKey.String(val) +} + +// Enum values for deployment.status +var ( + // failed + // Stability: development + DeploymentStatusFailed = DeploymentStatusKey.String("failed") + // succeeded + // Stability: development + DeploymentStatusSucceeded = DeploymentStatusKey.String("succeeded") +) + +// Namespace: destination +const ( + // DestinationAddressKey is the attribute Key conforming to the + // "destination.address" semantic conventions. It represents the destination + // address - domain name if available without reverse DNS lookup; otherwise, IP + // address or Unix domain socket name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "destination.example.com", "10.1.2.80", "/tmp/my.sock" + // Note: When observed from the source side, and when communicating through an + // intermediary, `destination.address` SHOULD represent the destination address + // behind any intermediaries, for example proxies, if it's available. + DestinationAddressKey = attribute.Key("destination.address") + + // DestinationPortKey is the attribute Key conforming to the "destination.port" + // semantic conventions. It represents the destination port number. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 3389, 2888 + DestinationPortKey = attribute.Key("destination.port") +) + +// DestinationAddress returns an attribute KeyValue conforming to the +// "destination.address" semantic conventions. It represents the destination +// address - domain name if available without reverse DNS lookup; otherwise, IP +// address or Unix domain socket name. +func DestinationAddress(val string) attribute.KeyValue { + return DestinationAddressKey.String(val) +} + +// DestinationPort returns an attribute KeyValue conforming to the +// "destination.port" semantic conventions. It represents the destination port +// number. +func DestinationPort(val int) attribute.KeyValue { + return DestinationPortKey.Int(val) +} + +// Namespace: device +const ( + // DeviceIDKey is the attribute Key conforming to the "device.id" semantic + // conventions. It represents a unique identifier representing the device. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "123456789012345", "01:23:45:67:89:AB" + // Note: Its value SHOULD be identical for all apps on a device and it SHOULD + // NOT change if an app is uninstalled and re-installed. + // However, it might be resettable by the user for all apps on a device. + // Hardware IDs (e.g. vendor-specific serial number, IMEI or MAC address) MAY be + // used as values. + // + // More information about Android identifier best practices can be found in the + // [Android user data IDs guide]. + // + // > [!WARNING]> This attribute may contain sensitive (PII) information. Caution + // > should be taken when storing personal data or anything which can identify a + // > user. GDPR and data protection laws may apply, + // > ensure you do your own due diligence.> Due to these reasons, this + // > identifier is not recommended for consumer applications and will likely + // > result in rejection from both Google Play and App Store. + // > However, it may be appropriate for specific enterprise scenarios, such as + // > kiosk devices or enterprise-managed devices, with appropriate compliance + // > clearance. + // > Any instrumentation providing this identifier MUST implement it as an + // > opt-in feature.> See [`app.installation.id`]> for a more + // > privacy-preserving alternative. + // + // [Android user data IDs guide]: https://developer.android.com/training/articles/user-data-ids + // [`app.installation.id`]: /docs/registry/attributes/app.md#app-installation-id + DeviceIDKey = attribute.Key("device.id") + + // DeviceManufacturerKey is the attribute Key conforming to the + // "device.manufacturer" semantic conventions. It represents the name of the + // device manufacturer. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Apple", "Samsung" + // Note: The Android OS provides this field via [Build]. iOS apps SHOULD + // hardcode the value `Apple`. + // + // [Build]: https://developer.android.com/reference/android/os/Build#MANUFACTURER + DeviceManufacturerKey = attribute.Key("device.manufacturer") + + // DeviceModelIdentifierKey is the attribute Key conforming to the + // "device.model.identifier" semantic conventions. It represents the model + // identifier for the device. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "iPhone3,4", "SM-G920F" + // Note: It's recommended this value represents a machine-readable version of + // the model identifier rather than the market or consumer-friendly name of the + // device. + DeviceModelIdentifierKey = attribute.Key("device.model.identifier") + + // DeviceModelNameKey is the attribute Key conforming to the "device.model.name" + // semantic conventions. It represents the marketing name for the device model. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "iPhone 6s Plus", "Samsung Galaxy S6" + // Note: It's recommended this value represents a human-readable version of the + // device model rather than a machine-readable alternative. + DeviceModelNameKey = attribute.Key("device.model.name") +) + +// DeviceID returns an attribute KeyValue conforming to the "device.id" semantic +// conventions. It represents a unique identifier representing the device. +func DeviceID(val string) attribute.KeyValue { + return DeviceIDKey.String(val) +} + +// DeviceManufacturer returns an attribute KeyValue conforming to the +// "device.manufacturer" semantic conventions. It represents the name of the +// device manufacturer. +func DeviceManufacturer(val string) attribute.KeyValue { + return DeviceManufacturerKey.String(val) +} + +// DeviceModelIdentifier returns an attribute KeyValue conforming to the +// "device.model.identifier" semantic conventions. It represents the model +// identifier for the device. +func DeviceModelIdentifier(val string) attribute.KeyValue { + return DeviceModelIdentifierKey.String(val) +} + +// DeviceModelName returns an attribute KeyValue conforming to the +// "device.model.name" semantic conventions. It represents the marketing name for +// the device model. +func DeviceModelName(val string) attribute.KeyValue { + return DeviceModelNameKey.String(val) +} + +// Namespace: disk +const ( + // DiskIODirectionKey is the attribute Key conforming to the "disk.io.direction" + // semantic conventions. It represents the disk IO operation direction. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "read" + DiskIODirectionKey = attribute.Key("disk.io.direction") +) + +// Enum values for disk.io.direction +var ( + // read + // Stability: development + DiskIODirectionRead = DiskIODirectionKey.String("read") + // write + // Stability: development + DiskIODirectionWrite = DiskIODirectionKey.String("write") +) + +// Namespace: dns +const ( + // DNSAnswersKey is the attribute Key conforming to the "dns.answers" semantic + // conventions. It represents the list of IPv4 or IPv6 addresses resolved during + // DNS lookup. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "10.0.0.1", "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + DNSAnswersKey = attribute.Key("dns.answers") + + // DNSQuestionNameKey is the attribute Key conforming to the "dns.question.name" + // semantic conventions. It represents the name being queried. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "www.example.com", "opentelemetry.io" + // Note: The name represents the queried domain name as it appears in the DNS + // query without any additional normalization. + DNSQuestionNameKey = attribute.Key("dns.question.name") +) + +// DNSAnswers returns an attribute KeyValue conforming to the "dns.answers" +// semantic conventions. It represents the list of IPv4 or IPv6 addresses +// resolved during DNS lookup. +func DNSAnswers(val ...string) attribute.KeyValue { + return DNSAnswersKey.StringSlice(val) +} + +// DNSQuestionName returns an attribute KeyValue conforming to the +// "dns.question.name" semantic conventions. It represents the name being +// queried. +func DNSQuestionName(val string) attribute.KeyValue { + return DNSQuestionNameKey.String(val) +} + +// Namespace: elasticsearch +const ( + // ElasticsearchNodeNameKey is the attribute Key conforming to the + // "elasticsearch.node.name" semantic conventions. It represents the represents + // the human-readable identifier of the node/instance to which a request was + // routed. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "instance-0000000001" + ElasticsearchNodeNameKey = attribute.Key("elasticsearch.node.name") +) + +// ElasticsearchNodeName returns an attribute KeyValue conforming to the +// "elasticsearch.node.name" semantic conventions. It represents the represents +// the human-readable identifier of the node/instance to which a request was +// routed. +func ElasticsearchNodeName(val string) attribute.KeyValue { + return ElasticsearchNodeNameKey.String(val) +} + +// Namespace: enduser +const ( + // EnduserIDKey is the attribute Key conforming to the "enduser.id" semantic + // conventions. It represents the unique identifier of an end user in the + // system. It maybe a username, email address, or other identifier. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "username" + // Note: Unique identifier of an end user in the system. + // + // > [!Warning] + // > This field contains sensitive (PII) information. + EnduserIDKey = attribute.Key("enduser.id") + + // EnduserPseudoIDKey is the attribute Key conforming to the "enduser.pseudo.id" + // semantic conventions. It represents the pseudonymous identifier of an end + // user. This identifier should be a random value that is not directly linked or + // associated with the end user's actual identity. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "QdH5CAWJgqVT4rOr0qtumf" + // Note: Pseudonymous identifier of an end user. + // + // > [!Warning] + // > This field contains sensitive (linkable PII) information. + EnduserPseudoIDKey = attribute.Key("enduser.pseudo.id") +) + +// EnduserID returns an attribute KeyValue conforming to the "enduser.id" +// semantic conventions. It represents the unique identifier of an end user in +// the system. It maybe a username, email address, or other identifier. +func EnduserID(val string) attribute.KeyValue { + return EnduserIDKey.String(val) +} + +// EnduserPseudoID returns an attribute KeyValue conforming to the +// "enduser.pseudo.id" semantic conventions. It represents the pseudonymous +// identifier of an end user. This identifier should be a random value that is +// not directly linked or associated with the end user's actual identity. +func EnduserPseudoID(val string) attribute.KeyValue { + return EnduserPseudoIDKey.String(val) +} + +// Namespace: error +const ( + // ErrorMessageKey is the attribute Key conforming to the "error.message" + // semantic conventions. It represents a message providing more detail about an + // error in human-readable form. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Unexpected input type: string", "The user has exceeded their + // storage quota" + // Note: `error.message` should provide additional context and detail about an + // error. + // It is NOT RECOMMENDED to duplicate the value of `error.type` in + // `error.message`. + // It is also NOT RECOMMENDED to duplicate the value of `exception.message` in + // `error.message`. + // + // `error.message` is NOT RECOMMENDED for metrics or spans due to its unbounded + // cardinality and overlap with span status. + ErrorMessageKey = attribute.Key("error.message") + + // ErrorTypeKey is the attribute Key conforming to the "error.type" semantic + // conventions. It represents the describes a class of error the operation ended + // with. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "timeout", "java.net.UnknownHostException", + // "server_certificate_invalid", "500" + // Note: The `error.type` SHOULD be predictable, and SHOULD have low + // cardinality. + // + // When `error.type` is set to a type (e.g., an exception type), its + // canonical class name identifying the type within the artifact SHOULD be used. + // + // Instrumentations SHOULD document the list of errors they report. + // + // The cardinality of `error.type` within one instrumentation library SHOULD be + // low. + // Telemetry consumers that aggregate data from multiple instrumentation + // libraries and applications + // should be prepared for `error.type` to have high cardinality at query time + // when no + // additional filters are applied. + // + // If the operation has completed successfully, instrumentations SHOULD NOT set + // `error.type`. + // + // If a specific domain defines its own set of error identifiers (such as HTTP + // or RPC status codes), + // it's RECOMMENDED to: + // + // - Use a domain-specific attribute + // - Set `error.type` to capture all errors, regardless of whether they are + // defined within the domain-specific set or not. + ErrorTypeKey = attribute.Key("error.type") +) + +// ErrorMessage returns an attribute KeyValue conforming to the "error.message" +// semantic conventions. It represents a message providing more detail about an +// error in human-readable form. +func ErrorMessage(val string) attribute.KeyValue { + return ErrorMessageKey.String(val) +} + +// Enum values for error.type +var ( + // A fallback error value to be used when the instrumentation doesn't define a + // custom value. + // + // Stability: stable + ErrorTypeOther = ErrorTypeKey.String("_OTHER") +) + +// Namespace: exception +const ( + // ExceptionMessageKey is the attribute Key conforming to the + // "exception.message" semantic conventions. It represents the exception + // message. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "Division by zero", "Can't convert 'int' object to str implicitly" + ExceptionMessageKey = attribute.Key("exception.message") + + // ExceptionStacktraceKey is the attribute Key conforming to the + // "exception.stacktrace" semantic conventions. It represents a stacktrace as a + // string in the natural representation for the language runtime. The + // representation is to be determined and documented by each language SIG. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: Exception in thread "main" java.lang.RuntimeException: Test + // exception\n at com.example.GenerateTrace.methodB(GenerateTrace.java:13)\n at + // com.example.GenerateTrace.methodA(GenerateTrace.java:9)\n at + // com.example.GenerateTrace.main(GenerateTrace.java:5) + ExceptionStacktraceKey = attribute.Key("exception.stacktrace") + + // ExceptionTypeKey is the attribute Key conforming to the "exception.type" + // semantic conventions. It represents the type of the exception (its + // fully-qualified class name, if applicable). The dynamic type of the exception + // should be preferred over the static type in languages that support it. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "java.net.ConnectException", "OSError" + ExceptionTypeKey = attribute.Key("exception.type") +) + +// ExceptionMessage returns an attribute KeyValue conforming to the +// "exception.message" semantic conventions. It represents the exception message. +func ExceptionMessage(val string) attribute.KeyValue { + return ExceptionMessageKey.String(val) +} + +// ExceptionStacktrace returns an attribute KeyValue conforming to the +// "exception.stacktrace" semantic conventions. It represents a stacktrace as a +// string in the natural representation for the language runtime. The +// representation is to be determined and documented by each language SIG. +func ExceptionStacktrace(val string) attribute.KeyValue { + return ExceptionStacktraceKey.String(val) +} + +// ExceptionType returns an attribute KeyValue conforming to the "exception.type" +// semantic conventions. It represents the type of the exception (its +// fully-qualified class name, if applicable). The dynamic type of the exception +// should be preferred over the static type in languages that support it. +func ExceptionType(val string) attribute.KeyValue { + return ExceptionTypeKey.String(val) +} + +// Namespace: faas +const ( + // FaaSColdstartKey is the attribute Key conforming to the "faas.coldstart" + // semantic conventions. It represents a boolean that is true if the serverless + // function is executed for the first time (aka cold-start). + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + FaaSColdstartKey = attribute.Key("faas.coldstart") + + // FaaSCronKey is the attribute Key conforming to the "faas.cron" semantic + // conventions. It represents a string containing the schedule period as + // [Cron Expression]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 0/5 * * * ? * + // + // [Cron Expression]: https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm + FaaSCronKey = attribute.Key("faas.cron") + + // FaaSDocumentCollectionKey is the attribute Key conforming to the + // "faas.document.collection" semantic conventions. It represents the name of + // the source on which the triggering operation was performed. For example, in + // Cloud Storage or S3 corresponds to the bucket name, and in Cosmos DB to the + // database name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "myBucketName", "myDbName" + FaaSDocumentCollectionKey = attribute.Key("faas.document.collection") + + // FaaSDocumentNameKey is the attribute Key conforming to the + // "faas.document.name" semantic conventions. It represents the document + // name/table subjected to the operation. For example, in Cloud Storage or S3 is + // the name of the file, and in Cosmos DB the table name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "myFile.txt", "myTableName" + FaaSDocumentNameKey = attribute.Key("faas.document.name") + + // FaaSDocumentOperationKey is the attribute Key conforming to the + // "faas.document.operation" semantic conventions. It represents the describes + // the type of the operation that was performed on the data. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + FaaSDocumentOperationKey = attribute.Key("faas.document.operation") + + // FaaSDocumentTimeKey is the attribute Key conforming to the + // "faas.document.time" semantic conventions. It represents a string containing + // the time when the data was accessed in the [ISO 8601] format expressed in + // [UTC]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 2020-01-23T13:47:06Z + // + // [ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html + // [UTC]: https://www.w3.org/TR/NOTE-datetime + FaaSDocumentTimeKey = attribute.Key("faas.document.time") + + // FaaSInstanceKey is the attribute Key conforming to the "faas.instance" + // semantic conventions. It represents the execution environment ID as a string, + // that will be potentially reused for other invocations to the same + // function/function version. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2021/06/28/[$LATEST]2f399eb14537447da05ab2a2e39309de" + // Note: - **AWS Lambda:** Use the (full) log stream name. + FaaSInstanceKey = attribute.Key("faas.instance") + + // FaaSInvocationIDKey is the attribute Key conforming to the + // "faas.invocation_id" semantic conventions. It represents the invocation ID of + // the current function invocation. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: af9d5aa4-a685-4c5f-a22b-444f80b3cc28 + FaaSInvocationIDKey = attribute.Key("faas.invocation_id") + + // FaaSInvokedNameKey is the attribute Key conforming to the "faas.invoked_name" + // semantic conventions. It represents the name of the invoked function. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: my-function + // Note: SHOULD be equal to the `faas.name` resource attribute of the invoked + // function. + FaaSInvokedNameKey = attribute.Key("faas.invoked_name") + + // FaaSInvokedProviderKey is the attribute Key conforming to the + // "faas.invoked_provider" semantic conventions. It represents the cloud + // provider of the invoked function. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: SHOULD be equal to the `cloud.provider` resource attribute of the + // invoked function. + FaaSInvokedProviderKey = attribute.Key("faas.invoked_provider") + + // FaaSInvokedRegionKey is the attribute Key conforming to the + // "faas.invoked_region" semantic conventions. It represents the cloud region of + // the invoked function. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: eu-central-1 + // Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked + // function. + FaaSInvokedRegionKey = attribute.Key("faas.invoked_region") + + // FaaSMaxMemoryKey is the attribute Key conforming to the "faas.max_memory" + // semantic conventions. It represents the amount of memory available to the + // serverless function converted to Bytes. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Note: It's recommended to set this attribute since e.g. too little memory can + // easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, + // the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this + // information (which must be multiplied by 1,048,576). + FaaSMaxMemoryKey = attribute.Key("faas.max_memory") + + // FaaSNameKey is the attribute Key conforming to the "faas.name" semantic + // conventions. It represents the name of the single function that this runtime + // instance executes. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-function", "myazurefunctionapp/some-function-name" + // Note: This is the name of the function as configured/deployed on the FaaS + // platform and is usually different from the name of the callback + // function (which may be stored in the + // [`code.namespace`/`code.function.name`] + // span attributes). + // + // For some cloud providers, the above definition is ambiguous. The following + // definition of function name MUST be used for this attribute + // (and consequently the span name) for the listed cloud providers/products: + // + // - **Azure:** The full name `/`, i.e., function app name + // followed by a forward slash followed by the function name (this form + // can also be seen in the resource JSON for the function). + // This means that a span attribute MUST be used, as an Azure function + // app can host multiple functions that would usually share + // a TracerProvider (see also the `cloud.resource_id` attribute). + // + // + // [`code.namespace`/`code.function.name`]: /docs/general/attributes.md#source-code-attributes + FaaSNameKey = attribute.Key("faas.name") + + // FaaSTimeKey is the attribute Key conforming to the "faas.time" semantic + // conventions. It represents a string containing the function invocation time + // in the [ISO 8601] format expressed in [UTC]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 2020-01-23T13:47:06Z + // + // [ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html + // [UTC]: https://www.w3.org/TR/NOTE-datetime + FaaSTimeKey = attribute.Key("faas.time") + + // FaaSTriggerKey is the attribute Key conforming to the "faas.trigger" semantic + // conventions. It represents the type of the trigger which caused this function + // invocation. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + FaaSTriggerKey = attribute.Key("faas.trigger") + + // FaaSVersionKey is the attribute Key conforming to the "faas.version" semantic + // conventions. It represents the immutable version of the function being + // executed. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "26", "pinkfroid-00002" + // Note: Depending on the cloud provider and platform, use: + // + // - **AWS Lambda:** The [function version] + // (an integer represented as a decimal string). + // - **Google Cloud Run (Services):** The [revision] + // (i.e., the function name plus the revision suffix). + // - **Google Cloud Functions:** The value of the + // [`K_REVISION` environment variable]. + // - **Azure Functions:** Not applicable. Do not set this attribute. + // + // + // [function version]: https://docs.aws.amazon.com/lambda/latest/dg/configuration-versions.html + // [revision]: https://cloud.google.com/run/docs/managing/revisions + // [`K_REVISION` environment variable]: https://cloud.google.com/run/docs/container-contract#services-env-vars + FaaSVersionKey = attribute.Key("faas.version") +) + +// FaaSColdstart returns an attribute KeyValue conforming to the "faas.coldstart" +// semantic conventions. It represents a boolean that is true if the serverless +// function is executed for the first time (aka cold-start). +func FaaSColdstart(val bool) attribute.KeyValue { + return FaaSColdstartKey.Bool(val) +} + +// FaaSCron returns an attribute KeyValue conforming to the "faas.cron" semantic +// conventions. It represents a string containing the schedule period as +// [Cron Expression]. +// +// [Cron Expression]: https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm +func FaaSCron(val string) attribute.KeyValue { + return FaaSCronKey.String(val) +} + +// FaaSDocumentCollection returns an attribute KeyValue conforming to the +// "faas.document.collection" semantic conventions. It represents the name of the +// source on which the triggering operation was performed. For example, in Cloud +// Storage or S3 corresponds to the bucket name, and in Cosmos DB to the database +// name. +func FaaSDocumentCollection(val string) attribute.KeyValue { + return FaaSDocumentCollectionKey.String(val) +} + +// FaaSDocumentName returns an attribute KeyValue conforming to the +// "faas.document.name" semantic conventions. It represents the document +// name/table subjected to the operation. For example, in Cloud Storage or S3 is +// the name of the file, and in Cosmos DB the table name. +func FaaSDocumentName(val string) attribute.KeyValue { + return FaaSDocumentNameKey.String(val) +} + +// FaaSDocumentTime returns an attribute KeyValue conforming to the +// "faas.document.time" semantic conventions. It represents a string containing +// the time when the data was accessed in the [ISO 8601] format expressed in +// [UTC]. +// +// [ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html +// [UTC]: https://www.w3.org/TR/NOTE-datetime +func FaaSDocumentTime(val string) attribute.KeyValue { + return FaaSDocumentTimeKey.String(val) +} + +// FaaSInstance returns an attribute KeyValue conforming to the "faas.instance" +// semantic conventions. It represents the execution environment ID as a string, +// that will be potentially reused for other invocations to the same +// function/function version. +func FaaSInstance(val string) attribute.KeyValue { + return FaaSInstanceKey.String(val) +} + +// FaaSInvocationID returns an attribute KeyValue conforming to the +// "faas.invocation_id" semantic conventions. It represents the invocation ID of +// the current function invocation. +func FaaSInvocationID(val string) attribute.KeyValue { + return FaaSInvocationIDKey.String(val) +} + +// FaaSInvokedName returns an attribute KeyValue conforming to the +// "faas.invoked_name" semantic conventions. It represents the name of the +// invoked function. +func FaaSInvokedName(val string) attribute.KeyValue { + return FaaSInvokedNameKey.String(val) +} + +// FaaSInvokedRegion returns an attribute KeyValue conforming to the +// "faas.invoked_region" semantic conventions. It represents the cloud region of +// the invoked function. +func FaaSInvokedRegion(val string) attribute.KeyValue { + return FaaSInvokedRegionKey.String(val) +} + +// FaaSMaxMemory returns an attribute KeyValue conforming to the +// "faas.max_memory" semantic conventions. It represents the amount of memory +// available to the serverless function converted to Bytes. +func FaaSMaxMemory(val int) attribute.KeyValue { + return FaaSMaxMemoryKey.Int(val) +} + +// FaaSName returns an attribute KeyValue conforming to the "faas.name" semantic +// conventions. It represents the name of the single function that this runtime +// instance executes. +func FaaSName(val string) attribute.KeyValue { + return FaaSNameKey.String(val) +} + +// FaaSTime returns an attribute KeyValue conforming to the "faas.time" semantic +// conventions. It represents a string containing the function invocation time in +// the [ISO 8601] format expressed in [UTC]. +// +// [ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html +// [UTC]: https://www.w3.org/TR/NOTE-datetime +func FaaSTime(val string) attribute.KeyValue { + return FaaSTimeKey.String(val) +} + +// FaaSVersion returns an attribute KeyValue conforming to the "faas.version" +// semantic conventions. It represents the immutable version of the function +// being executed. +func FaaSVersion(val string) attribute.KeyValue { + return FaaSVersionKey.String(val) +} + +// Enum values for faas.document.operation +var ( + // When a new object is created. + // Stability: development + FaaSDocumentOperationInsert = FaaSDocumentOperationKey.String("insert") + // When an object is modified. + // Stability: development + FaaSDocumentOperationEdit = FaaSDocumentOperationKey.String("edit") + // When an object is deleted. + // Stability: development + FaaSDocumentOperationDelete = FaaSDocumentOperationKey.String("delete") +) + +// Enum values for faas.invoked_provider +var ( + // Alibaba Cloud + // Stability: development + FaaSInvokedProviderAlibabaCloud = FaaSInvokedProviderKey.String("alibaba_cloud") + // Amazon Web Services + // Stability: development + FaaSInvokedProviderAWS = FaaSInvokedProviderKey.String("aws") + // Microsoft Azure + // Stability: development + FaaSInvokedProviderAzure = FaaSInvokedProviderKey.String("azure") + // Google Cloud Platform + // Stability: development + FaaSInvokedProviderGCP = FaaSInvokedProviderKey.String("gcp") + // Tencent Cloud + // Stability: development + FaaSInvokedProviderTencentCloud = FaaSInvokedProviderKey.String("tencent_cloud") +) + +// Enum values for faas.trigger +var ( + // A response to some data source operation such as a database or filesystem + // read/write + // Stability: development + FaaSTriggerDatasource = FaaSTriggerKey.String("datasource") + // To provide an answer to an inbound HTTP request + // Stability: development + FaaSTriggerHTTP = FaaSTriggerKey.String("http") + // A function is set to be executed when messages are sent to a messaging system + // Stability: development + FaaSTriggerPubSub = FaaSTriggerKey.String("pubsub") + // A function is scheduled to be executed regularly + // Stability: development + FaaSTriggerTimer = FaaSTriggerKey.String("timer") + // If none of the others apply + // Stability: development + FaaSTriggerOther = FaaSTriggerKey.String("other") +) + +// Namespace: feature_flag +const ( + // FeatureFlagContextIDKey is the attribute Key conforming to the + // "feature_flag.context.id" semantic conventions. It represents the unique + // identifier for the flag evaluation context. For example, the targeting key. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Release_Candidate + // + // Examples: "5157782b-2203-4c80-a857-dbbd5e7761db" + FeatureFlagContextIDKey = attribute.Key("feature_flag.context.id") + + // FeatureFlagKeyKey is the attribute Key conforming to the "feature_flag.key" + // semantic conventions. It represents the lookup key of the feature flag. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Release_Candidate + // + // Examples: "logo-color" + FeatureFlagKeyKey = attribute.Key("feature_flag.key") + + // FeatureFlagProviderNameKey is the attribute Key conforming to the + // "feature_flag.provider.name" semantic conventions. It represents the + // identifies the feature flag provider. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Release_Candidate + // + // Examples: "Flag Manager" + FeatureFlagProviderNameKey = attribute.Key("feature_flag.provider.name") + + // FeatureFlagResultReasonKey is the attribute Key conforming to the + // "feature_flag.result.reason" semantic conventions. It represents the reason + // code which shows how a feature flag value was determined. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Release_Candidate + // + // Examples: "static", "targeting_match", "error", "default" + FeatureFlagResultReasonKey = attribute.Key("feature_flag.result.reason") + + // FeatureFlagResultValueKey is the attribute Key conforming to the + // "feature_flag.result.value" semantic conventions. It represents the evaluated + // value of the feature flag. + // + // Type: any + // RequirementLevel: Recommended + // Stability: Release_Candidate + // + // Examples: "#ff0000", true, 3 + // Note: With some feature flag providers, feature flag results can be quite + // large or contain private or sensitive details. + // Because of this, `feature_flag.result.variant` is often the preferred + // attribute if it is available. + // + // It may be desirable to redact or otherwise limit the size and scope of + // `feature_flag.result.value` if possible. + // Because the evaluated flag value is unstructured and may be any type, it is + // left to the instrumentation author to determine how best to achieve this. + FeatureFlagResultValueKey = attribute.Key("feature_flag.result.value") + + // FeatureFlagResultVariantKey is the attribute Key conforming to the + // "feature_flag.result.variant" semantic conventions. It represents a semantic + // identifier for an evaluated flag value. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Release_Candidate + // + // Examples: "red", "true", "on" + // Note: A semantic identifier, commonly referred to as a variant, provides a + // means + // for referring to a value without including the value itself. This can + // provide additional context for understanding the meaning behind a value. + // For example, the variant `red` maybe be used for the value `#c05543`. + FeatureFlagResultVariantKey = attribute.Key("feature_flag.result.variant") + + // FeatureFlagSetIDKey is the attribute Key conforming to the + // "feature_flag.set.id" semantic conventions. It represents the identifier of + // the [flag set] to which the feature flag belongs. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Release_Candidate + // + // Examples: "proj-1", "ab98sgs", "service1/dev" + // + // [flag set]: https://openfeature.dev/specification/glossary/#flag-set + FeatureFlagSetIDKey = attribute.Key("feature_flag.set.id") + + // FeatureFlagVersionKey is the attribute Key conforming to the + // "feature_flag.version" semantic conventions. It represents the version of the + // ruleset used during the evaluation. This may be any stable value which + // uniquely identifies the ruleset. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Release_Candidate + // + // Examples: "1", "01ABCDEF" + FeatureFlagVersionKey = attribute.Key("feature_flag.version") +) + +// FeatureFlagContextID returns an attribute KeyValue conforming to the +// "feature_flag.context.id" semantic conventions. It represents the unique +// identifier for the flag evaluation context. For example, the targeting key. +func FeatureFlagContextID(val string) attribute.KeyValue { + return FeatureFlagContextIDKey.String(val) +} + +// FeatureFlagKey returns an attribute KeyValue conforming to the +// "feature_flag.key" semantic conventions. It represents the lookup key of the +// feature flag. +func FeatureFlagKey(val string) attribute.KeyValue { + return FeatureFlagKeyKey.String(val) +} + +// FeatureFlagProviderName returns an attribute KeyValue conforming to the +// "feature_flag.provider.name" semantic conventions. It represents the +// identifies the feature flag provider. +func FeatureFlagProviderName(val string) attribute.KeyValue { + return FeatureFlagProviderNameKey.String(val) +} + +// FeatureFlagResultVariant returns an attribute KeyValue conforming to the +// "feature_flag.result.variant" semantic conventions. It represents a semantic +// identifier for an evaluated flag value. +func FeatureFlagResultVariant(val string) attribute.KeyValue { + return FeatureFlagResultVariantKey.String(val) +} + +// FeatureFlagSetID returns an attribute KeyValue conforming to the +// "feature_flag.set.id" semantic conventions. It represents the identifier of +// the [flag set] to which the feature flag belongs. +// +// [flag set]: https://openfeature.dev/specification/glossary/#flag-set +func FeatureFlagSetID(val string) attribute.KeyValue { + return FeatureFlagSetIDKey.String(val) +} + +// FeatureFlagVersion returns an attribute KeyValue conforming to the +// "feature_flag.version" semantic conventions. It represents the version of the +// ruleset used during the evaluation. This may be any stable value which +// uniquely identifies the ruleset. +func FeatureFlagVersion(val string) attribute.KeyValue { + return FeatureFlagVersionKey.String(val) +} + +// Enum values for feature_flag.result.reason +var ( + // The resolved value is static (no dynamic evaluation). + // Stability: release_candidate + FeatureFlagResultReasonStatic = FeatureFlagResultReasonKey.String("static") + // The resolved value fell back to a pre-configured value (no dynamic evaluation + // occurred or dynamic evaluation yielded no result). + // Stability: release_candidate + FeatureFlagResultReasonDefault = FeatureFlagResultReasonKey.String("default") + // The resolved value was the result of a dynamic evaluation, such as a rule or + // specific user-targeting. + // Stability: release_candidate + FeatureFlagResultReasonTargetingMatch = FeatureFlagResultReasonKey.String("targeting_match") + // The resolved value was the result of pseudorandom assignment. + // Stability: release_candidate + FeatureFlagResultReasonSplit = FeatureFlagResultReasonKey.String("split") + // The resolved value was retrieved from cache. + // Stability: release_candidate + FeatureFlagResultReasonCached = FeatureFlagResultReasonKey.String("cached") + // The resolved value was the result of the flag being disabled in the + // management system. + // Stability: release_candidate + FeatureFlagResultReasonDisabled = FeatureFlagResultReasonKey.String("disabled") + // The reason for the resolved value could not be determined. + // Stability: release_candidate + FeatureFlagResultReasonUnknown = FeatureFlagResultReasonKey.String("unknown") + // The resolved value is non-authoritative or possibly out of date + // Stability: release_candidate + FeatureFlagResultReasonStale = FeatureFlagResultReasonKey.String("stale") + // The resolved value was the result of an error. + // Stability: release_candidate + FeatureFlagResultReasonError = FeatureFlagResultReasonKey.String("error") +) + +// Namespace: file +const ( + // FileAccessedKey is the attribute Key conforming to the "file.accessed" + // semantic conventions. It represents the time when the file was last accessed, + // in ISO 8601 format. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2021-01-01T12:00:00Z" + // Note: This attribute might not be supported by some file systems — NFS, + // FAT32, in embedded OS, etc. + FileAccessedKey = attribute.Key("file.accessed") + + // FileAttributesKey is the attribute Key conforming to the "file.attributes" + // semantic conventions. It represents the array of file attributes. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "readonly", "hidden" + // Note: Attributes names depend on the OS or file system. Here’s a + // non-exhaustive list of values expected for this attribute: `archive`, + // `compressed`, `directory`, `encrypted`, `execute`, `hidden`, `immutable`, + // `journaled`, `read`, `readonly`, `symbolic link`, `system`, `temporary`, + // `write`. + FileAttributesKey = attribute.Key("file.attributes") + + // FileChangedKey is the attribute Key conforming to the "file.changed" semantic + // conventions. It represents the time when the file attributes or metadata was + // last changed, in ISO 8601 format. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2021-01-01T12:00:00Z" + // Note: `file.changed` captures the time when any of the file's properties or + // attributes (including the content) are changed, while `file.modified` + // captures the timestamp when the file content is modified. + FileChangedKey = attribute.Key("file.changed") + + // FileCreatedKey is the attribute Key conforming to the "file.created" semantic + // conventions. It represents the time when the file was created, in ISO 8601 + // format. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2021-01-01T12:00:00Z" + // Note: This attribute might not be supported by some file systems — NFS, + // FAT32, in embedded OS, etc. + FileCreatedKey = attribute.Key("file.created") + + // FileDirectoryKey is the attribute Key conforming to the "file.directory" + // semantic conventions. It represents the directory where the file is located. + // It should include the drive letter, when appropriate. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/home/user", "C:\Program Files\MyApp" + FileDirectoryKey = attribute.Key("file.directory") + + // FileExtensionKey is the attribute Key conforming to the "file.extension" + // semantic conventions. It represents the file extension, excluding the leading + // dot. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "png", "gz" + // Note: When the file name has multiple extensions (example.tar.gz), only the + // last one should be captured ("gz", not "tar.gz"). + FileExtensionKey = attribute.Key("file.extension") + + // FileForkNameKey is the attribute Key conforming to the "file.fork_name" + // semantic conventions. It represents the name of the fork. A fork is + // additional data associated with a filesystem object. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Zone.Identifier" + // Note: On Linux, a resource fork is used to store additional data with a + // filesystem object. A file always has at least one fork for the data portion, + // and additional forks may exist. + // On NTFS, this is analogous to an Alternate Data Stream (ADS), and the default + // data stream for a file is just called $DATA. Zone.Identifier is commonly used + // by Windows to track contents downloaded from the Internet. An ADS is + // typically of the form: C:\path\to\filename.extension:some_fork_name, and + // some_fork_name is the value that should populate `fork_name`. + // `filename.extension` should populate `file.name`, and `extension` should + // populate `file.extension`. The full path, `file.path`, will include the fork + // name. + FileForkNameKey = attribute.Key("file.fork_name") + + // FileGroupIDKey is the attribute Key conforming to the "file.group.id" + // semantic conventions. It represents the primary Group ID (GID) of the file. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "1000" + FileGroupIDKey = attribute.Key("file.group.id") + + // FileGroupNameKey is the attribute Key conforming to the "file.group.name" + // semantic conventions. It represents the primary group name of the file. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "users" + FileGroupNameKey = attribute.Key("file.group.name") + + // FileInodeKey is the attribute Key conforming to the "file.inode" semantic + // conventions. It represents the inode representing the file in the filesystem. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "256383" + FileInodeKey = attribute.Key("file.inode") + + // FileModeKey is the attribute Key conforming to the "file.mode" semantic + // conventions. It represents the mode of the file in octal representation. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "0640" + FileModeKey = attribute.Key("file.mode") + + // FileModifiedKey is the attribute Key conforming to the "file.modified" + // semantic conventions. It represents the time when the file content was last + // modified, in ISO 8601 format. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2021-01-01T12:00:00Z" + FileModifiedKey = attribute.Key("file.modified") + + // FileNameKey is the attribute Key conforming to the "file.name" semantic + // conventions. It represents the name of the file including the extension, + // without the directory. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "example.png" + FileNameKey = attribute.Key("file.name") + + // FileOwnerIDKey is the attribute Key conforming to the "file.owner.id" + // semantic conventions. It represents the user ID (UID) or security identifier + // (SID) of the file owner. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "1000" + FileOwnerIDKey = attribute.Key("file.owner.id") + + // FileOwnerNameKey is the attribute Key conforming to the "file.owner.name" + // semantic conventions. It represents the username of the file owner. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "root" + FileOwnerNameKey = attribute.Key("file.owner.name") + + // FilePathKey is the attribute Key conforming to the "file.path" semantic + // conventions. It represents the full path to the file, including the file + // name. It should include the drive letter, when appropriate. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/home/alice/example.png", "C:\Program Files\MyApp\myapp.exe" + FilePathKey = attribute.Key("file.path") + + // FileSizeKey is the attribute Key conforming to the "file.size" semantic + // conventions. It represents the file size in bytes. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + FileSizeKey = attribute.Key("file.size") + + // FileSymbolicLinkTargetPathKey is the attribute Key conforming to the + // "file.symbolic_link.target_path" semantic conventions. It represents the path + // to the target of a symbolic link. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/usr/bin/python3" + // Note: This attribute is only applicable to symbolic links. + FileSymbolicLinkTargetPathKey = attribute.Key("file.symbolic_link.target_path") +) + +// FileAccessed returns an attribute KeyValue conforming to the "file.accessed" +// semantic conventions. It represents the time when the file was last accessed, +// in ISO 8601 format. +func FileAccessed(val string) attribute.KeyValue { + return FileAccessedKey.String(val) +} + +// FileAttributes returns an attribute KeyValue conforming to the +// "file.attributes" semantic conventions. It represents the array of file +// attributes. +func FileAttributes(val ...string) attribute.KeyValue { + return FileAttributesKey.StringSlice(val) +} + +// FileChanged returns an attribute KeyValue conforming to the "file.changed" +// semantic conventions. It represents the time when the file attributes or +// metadata was last changed, in ISO 8601 format. +func FileChanged(val string) attribute.KeyValue { + return FileChangedKey.String(val) +} + +// FileCreated returns an attribute KeyValue conforming to the "file.created" +// semantic conventions. It represents the time when the file was created, in ISO +// 8601 format. +func FileCreated(val string) attribute.KeyValue { + return FileCreatedKey.String(val) +} + +// FileDirectory returns an attribute KeyValue conforming to the "file.directory" +// semantic conventions. It represents the directory where the file is located. +// It should include the drive letter, when appropriate. +func FileDirectory(val string) attribute.KeyValue { + return FileDirectoryKey.String(val) +} + +// FileExtension returns an attribute KeyValue conforming to the "file.extension" +// semantic conventions. It represents the file extension, excluding the leading +// dot. +func FileExtension(val string) attribute.KeyValue { + return FileExtensionKey.String(val) +} + +// FileForkName returns an attribute KeyValue conforming to the "file.fork_name" +// semantic conventions. It represents the name of the fork. A fork is additional +// data associated with a filesystem object. +func FileForkName(val string) attribute.KeyValue { + return FileForkNameKey.String(val) +} + +// FileGroupID returns an attribute KeyValue conforming to the "file.group.id" +// semantic conventions. It represents the primary Group ID (GID) of the file. +func FileGroupID(val string) attribute.KeyValue { + return FileGroupIDKey.String(val) +} + +// FileGroupName returns an attribute KeyValue conforming to the +// "file.group.name" semantic conventions. It represents the primary group name +// of the file. +func FileGroupName(val string) attribute.KeyValue { + return FileGroupNameKey.String(val) +} + +// FileInode returns an attribute KeyValue conforming to the "file.inode" +// semantic conventions. It represents the inode representing the file in the +// filesystem. +func FileInode(val string) attribute.KeyValue { + return FileInodeKey.String(val) +} + +// FileMode returns an attribute KeyValue conforming to the "file.mode" semantic +// conventions. It represents the mode of the file in octal representation. +func FileMode(val string) attribute.KeyValue { + return FileModeKey.String(val) +} + +// FileModified returns an attribute KeyValue conforming to the "file.modified" +// semantic conventions. It represents the time when the file content was last +// modified, in ISO 8601 format. +func FileModified(val string) attribute.KeyValue { + return FileModifiedKey.String(val) +} + +// FileName returns an attribute KeyValue conforming to the "file.name" semantic +// conventions. It represents the name of the file including the extension, +// without the directory. +func FileName(val string) attribute.KeyValue { + return FileNameKey.String(val) +} + +// FileOwnerID returns an attribute KeyValue conforming to the "file.owner.id" +// semantic conventions. It represents the user ID (UID) or security identifier +// (SID) of the file owner. +func FileOwnerID(val string) attribute.KeyValue { + return FileOwnerIDKey.String(val) +} + +// FileOwnerName returns an attribute KeyValue conforming to the +// "file.owner.name" semantic conventions. It represents the username of the file +// owner. +func FileOwnerName(val string) attribute.KeyValue { + return FileOwnerNameKey.String(val) +} + +// FilePath returns an attribute KeyValue conforming to the "file.path" semantic +// conventions. It represents the full path to the file, including the file name. +// It should include the drive letter, when appropriate. +func FilePath(val string) attribute.KeyValue { + return FilePathKey.String(val) +} + +// FileSize returns an attribute KeyValue conforming to the "file.size" semantic +// conventions. It represents the file size in bytes. +func FileSize(val int) attribute.KeyValue { + return FileSizeKey.Int(val) +} + +// FileSymbolicLinkTargetPath returns an attribute KeyValue conforming to the +// "file.symbolic_link.target_path" semantic conventions. It represents the path +// to the target of a symbolic link. +func FileSymbolicLinkTargetPath(val string) attribute.KeyValue { + return FileSymbolicLinkTargetPathKey.String(val) +} + +// Namespace: gcp +const ( + // GCPAppHubApplicationContainerKey is the attribute Key conforming to the + // "gcp.apphub.application.container" semantic conventions. It represents the + // container within GCP where the AppHub application is defined. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "projects/my-container-project" + GCPAppHubApplicationContainerKey = attribute.Key("gcp.apphub.application.container") + + // GCPAppHubApplicationIDKey is the attribute Key conforming to the + // "gcp.apphub.application.id" semantic conventions. It represents the name of + // the application as configured in AppHub. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-application" + GCPAppHubApplicationIDKey = attribute.Key("gcp.apphub.application.id") + + // GCPAppHubApplicationLocationKey is the attribute Key conforming to the + // "gcp.apphub.application.location" semantic conventions. It represents the GCP + // zone or region where the application is defined. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "us-central1" + GCPAppHubApplicationLocationKey = attribute.Key("gcp.apphub.application.location") + + // GCPAppHubServiceCriticalityTypeKey is the attribute Key conforming to the + // "gcp.apphub.service.criticality_type" semantic conventions. It represents the + // criticality of a service indicates its importance to the business. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: [See AppHub type enum] + // + // [See AppHub type enum]: https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type + GCPAppHubServiceCriticalityTypeKey = attribute.Key("gcp.apphub.service.criticality_type") + + // GCPAppHubServiceEnvironmentTypeKey is the attribute Key conforming to the + // "gcp.apphub.service.environment_type" semantic conventions. It represents the + // environment of a service is the stage of a software lifecycle. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: [See AppHub environment type] + // + // [See AppHub environment type]: https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type_1 + GCPAppHubServiceEnvironmentTypeKey = attribute.Key("gcp.apphub.service.environment_type") + + // GCPAppHubServiceIDKey is the attribute Key conforming to the + // "gcp.apphub.service.id" semantic conventions. It represents the name of the + // service as configured in AppHub. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-service" + GCPAppHubServiceIDKey = attribute.Key("gcp.apphub.service.id") + + // GCPAppHubWorkloadCriticalityTypeKey is the attribute Key conforming to the + // "gcp.apphub.workload.criticality_type" semantic conventions. It represents + // the criticality of a workload indicates its importance to the business. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: [See AppHub type enum] + // + // [See AppHub type enum]: https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type + GCPAppHubWorkloadCriticalityTypeKey = attribute.Key("gcp.apphub.workload.criticality_type") + + // GCPAppHubWorkloadEnvironmentTypeKey is the attribute Key conforming to the + // "gcp.apphub.workload.environment_type" semantic conventions. It represents + // the environment of a workload is the stage of a software lifecycle. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: [See AppHub environment type] + // + // [See AppHub environment type]: https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type_1 + GCPAppHubWorkloadEnvironmentTypeKey = attribute.Key("gcp.apphub.workload.environment_type") + + // GCPAppHubWorkloadIDKey is the attribute Key conforming to the + // "gcp.apphub.workload.id" semantic conventions. It represents the name of the + // workload as configured in AppHub. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-workload" + GCPAppHubWorkloadIDKey = attribute.Key("gcp.apphub.workload.id") + + // GCPAppHubDestinationApplicationContainerKey is the attribute Key conforming + // to the "gcp.apphub_destination.application.container" semantic conventions. + // It represents the container within GCP where the AppHub destination + // application is defined. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "projects/my-container-project" + GCPAppHubDestinationApplicationContainerKey = attribute.Key("gcp.apphub_destination.application.container") + + // GCPAppHubDestinationApplicationIDKey is the attribute Key conforming to the + // "gcp.apphub_destination.application.id" semantic conventions. It represents + // the name of the destination application as configured in AppHub. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-application" + GCPAppHubDestinationApplicationIDKey = attribute.Key("gcp.apphub_destination.application.id") + + // GCPAppHubDestinationApplicationLocationKey is the attribute Key conforming to + // the "gcp.apphub_destination.application.location" semantic conventions. It + // represents the GCP zone or region where the destination application is + // defined. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "us-central1" + GCPAppHubDestinationApplicationLocationKey = attribute.Key("gcp.apphub_destination.application.location") + + // GCPAppHubDestinationServiceCriticalityTypeKey is the attribute Key conforming + // to the "gcp.apphub_destination.service.criticality_type" semantic + // conventions. It represents the criticality of a destination workload + // indicates its importance to the business as specified in [AppHub type enum]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // + // [AppHub type enum]: https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type + GCPAppHubDestinationServiceCriticalityTypeKey = attribute.Key("gcp.apphub_destination.service.criticality_type") + + // GCPAppHubDestinationServiceEnvironmentTypeKey is the attribute Key conforming + // to the "gcp.apphub_destination.service.environment_type" semantic + // conventions. It represents the software lifecycle stage of a destination + // service as defined [AppHub environment type]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // + // [AppHub environment type]: https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type_1 + GCPAppHubDestinationServiceEnvironmentTypeKey = attribute.Key("gcp.apphub_destination.service.environment_type") + + // GCPAppHubDestinationServiceIDKey is the attribute Key conforming to the + // "gcp.apphub_destination.service.id" semantic conventions. It represents the + // name of the destination service as configured in AppHub. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-service" + GCPAppHubDestinationServiceIDKey = attribute.Key("gcp.apphub_destination.service.id") + + // GCPAppHubDestinationWorkloadCriticalityTypeKey is the attribute Key + // conforming to the "gcp.apphub_destination.workload.criticality_type" semantic + // conventions. It represents the criticality of a destination workload + // indicates its importance to the business as specified in [AppHub type enum]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // + // [AppHub type enum]: https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type + GCPAppHubDestinationWorkloadCriticalityTypeKey = attribute.Key("gcp.apphub_destination.workload.criticality_type") + + // GCPAppHubDestinationWorkloadEnvironmentTypeKey is the attribute Key + // conforming to the "gcp.apphub_destination.workload.environment_type" semantic + // conventions. It represents the environment of a destination workload is the + // stage of a software lifecycle as provided in the [AppHub environment type]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // + // [AppHub environment type]: https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type_1 + GCPAppHubDestinationWorkloadEnvironmentTypeKey = attribute.Key("gcp.apphub_destination.workload.environment_type") + + // GCPAppHubDestinationWorkloadIDKey is the attribute Key conforming to the + // "gcp.apphub_destination.workload.id" semantic conventions. It represents the + // name of the destination workload as configured in AppHub. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-workload" + GCPAppHubDestinationWorkloadIDKey = attribute.Key("gcp.apphub_destination.workload.id") + + // GCPClientServiceKey is the attribute Key conforming to the + // "gcp.client.service" semantic conventions. It represents the identifies the + // Google Cloud service for which the official client library is intended. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "appengine", "run", "firestore", "alloydb", "spanner" + // Note: Intended to be a stable identifier for Google Cloud client libraries + // that is uniform across implementation languages. The value should be derived + // from the canonical service domain for the service; for example, + // 'foo.googleapis.com' should result in a value of 'foo'. + GCPClientServiceKey = attribute.Key("gcp.client.service") + + // GCPCloudRunJobExecutionKey is the attribute Key conforming to the + // "gcp.cloud_run.job.execution" semantic conventions. It represents the name of + // the Cloud Run [execution] being run for the Job, as set by the + // [`CLOUD_RUN_EXECUTION`] environment variable. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "job-name-xxxx", "sample-job-mdw84" + // + // [execution]: https://cloud.google.com/run/docs/managing/job-executions + // [`CLOUD_RUN_EXECUTION`]: https://cloud.google.com/run/docs/container-contract#jobs-env-vars + GCPCloudRunJobExecutionKey = attribute.Key("gcp.cloud_run.job.execution") + + // GCPCloudRunJobTaskIndexKey is the attribute Key conforming to the + // "gcp.cloud_run.job.task_index" semantic conventions. It represents the index + // for a task within an execution as provided by the [`CLOUD_RUN_TASK_INDEX`] + // environment variable. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 0, 1 + // + // [`CLOUD_RUN_TASK_INDEX`]: https://cloud.google.com/run/docs/container-contract#jobs-env-vars + GCPCloudRunJobTaskIndexKey = attribute.Key("gcp.cloud_run.job.task_index") + + // GCPGCEInstanceHostnameKey is the attribute Key conforming to the + // "gcp.gce.instance.hostname" semantic conventions. It represents the hostname + // of a GCE instance. This is the full value of the default or [custom hostname] + // . + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-host1234.example.com", + // "sample-vm.us-west1-b.c.my-project.internal" + // + // [custom hostname]: https://cloud.google.com/compute/docs/instances/custom-hostname-vm + GCPGCEInstanceHostnameKey = attribute.Key("gcp.gce.instance.hostname") + + // GCPGCEInstanceNameKey is the attribute Key conforming to the + // "gcp.gce.instance.name" semantic conventions. It represents the instance name + // of a GCE instance. This is the value provided by `host.name`, the visible + // name of the instance in the Cloud Console UI, and the prefix for the default + // hostname of the instance as defined by the [default internal DNS name]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "instance-1", "my-vm-name" + // + // [default internal DNS name]: https://cloud.google.com/compute/docs/internal-dns#instance-fully-qualified-domain-names + GCPGCEInstanceNameKey = attribute.Key("gcp.gce.instance.name") +) + +// GCPAppHubApplicationContainer returns an attribute KeyValue conforming to the +// "gcp.apphub.application.container" semantic conventions. It represents the +// container within GCP where the AppHub application is defined. +func GCPAppHubApplicationContainer(val string) attribute.KeyValue { + return GCPAppHubApplicationContainerKey.String(val) +} + +// GCPAppHubApplicationID returns an attribute KeyValue conforming to the +// "gcp.apphub.application.id" semantic conventions. It represents the name of +// the application as configured in AppHub. +func GCPAppHubApplicationID(val string) attribute.KeyValue { + return GCPAppHubApplicationIDKey.String(val) +} + +// GCPAppHubApplicationLocation returns an attribute KeyValue conforming to the +// "gcp.apphub.application.location" semantic conventions. It represents the GCP +// zone or region where the application is defined. +func GCPAppHubApplicationLocation(val string) attribute.KeyValue { + return GCPAppHubApplicationLocationKey.String(val) +} + +// GCPAppHubServiceID returns an attribute KeyValue conforming to the +// "gcp.apphub.service.id" semantic conventions. It represents the name of the +// service as configured in AppHub. +func GCPAppHubServiceID(val string) attribute.KeyValue { + return GCPAppHubServiceIDKey.String(val) +} + +// GCPAppHubWorkloadID returns an attribute KeyValue conforming to the +// "gcp.apphub.workload.id" semantic conventions. It represents the name of the +// workload as configured in AppHub. +func GCPAppHubWorkloadID(val string) attribute.KeyValue { + return GCPAppHubWorkloadIDKey.String(val) +} + +// GCPAppHubDestinationApplicationContainer returns an attribute KeyValue +// conforming to the "gcp.apphub_destination.application.container" semantic +// conventions. It represents the container within GCP where the AppHub +// destination application is defined. +func GCPAppHubDestinationApplicationContainer(val string) attribute.KeyValue { + return GCPAppHubDestinationApplicationContainerKey.String(val) +} + +// GCPAppHubDestinationApplicationID returns an attribute KeyValue conforming to +// the "gcp.apphub_destination.application.id" semantic conventions. It +// represents the name of the destination application as configured in AppHub. +func GCPAppHubDestinationApplicationID(val string) attribute.KeyValue { + return GCPAppHubDestinationApplicationIDKey.String(val) +} + +// GCPAppHubDestinationApplicationLocation returns an attribute KeyValue +// conforming to the "gcp.apphub_destination.application.location" semantic +// conventions. It represents the GCP zone or region where the destination +// application is defined. +func GCPAppHubDestinationApplicationLocation(val string) attribute.KeyValue { + return GCPAppHubDestinationApplicationLocationKey.String(val) +} + +// GCPAppHubDestinationServiceID returns an attribute KeyValue conforming to the +// "gcp.apphub_destination.service.id" semantic conventions. It represents the +// name of the destination service as configured in AppHub. +func GCPAppHubDestinationServiceID(val string) attribute.KeyValue { + return GCPAppHubDestinationServiceIDKey.String(val) +} + +// GCPAppHubDestinationWorkloadID returns an attribute KeyValue conforming to the +// "gcp.apphub_destination.workload.id" semantic conventions. It represents the +// name of the destination workload as configured in AppHub. +func GCPAppHubDestinationWorkloadID(val string) attribute.KeyValue { + return GCPAppHubDestinationWorkloadIDKey.String(val) +} + +// GCPClientService returns an attribute KeyValue conforming to the +// "gcp.client.service" semantic conventions. It represents the identifies the +// Google Cloud service for which the official client library is intended. +func GCPClientService(val string) attribute.KeyValue { + return GCPClientServiceKey.String(val) +} + +// GCPCloudRunJobExecution returns an attribute KeyValue conforming to the +// "gcp.cloud_run.job.execution" semantic conventions. It represents the name of +// the Cloud Run [execution] being run for the Job, as set by the +// [`CLOUD_RUN_EXECUTION`] environment variable. +// +// [execution]: https://cloud.google.com/run/docs/managing/job-executions +// [`CLOUD_RUN_EXECUTION`]: https://cloud.google.com/run/docs/container-contract#jobs-env-vars +func GCPCloudRunJobExecution(val string) attribute.KeyValue { + return GCPCloudRunJobExecutionKey.String(val) +} + +// GCPCloudRunJobTaskIndex returns an attribute KeyValue conforming to the +// "gcp.cloud_run.job.task_index" semantic conventions. It represents the index +// for a task within an execution as provided by the [`CLOUD_RUN_TASK_INDEX`] +// environment variable. +// +// [`CLOUD_RUN_TASK_INDEX`]: https://cloud.google.com/run/docs/container-contract#jobs-env-vars +func GCPCloudRunJobTaskIndex(val int) attribute.KeyValue { + return GCPCloudRunJobTaskIndexKey.Int(val) +} + +// GCPGCEInstanceHostname returns an attribute KeyValue conforming to the +// "gcp.gce.instance.hostname" semantic conventions. It represents the hostname +// of a GCE instance. This is the full value of the default or [custom hostname] +// . +// +// [custom hostname]: https://cloud.google.com/compute/docs/instances/custom-hostname-vm +func GCPGCEInstanceHostname(val string) attribute.KeyValue { + return GCPGCEInstanceHostnameKey.String(val) +} + +// GCPGCEInstanceName returns an attribute KeyValue conforming to the +// "gcp.gce.instance.name" semantic conventions. It represents the instance name +// of a GCE instance. This is the value provided by `host.name`, the visible name +// of the instance in the Cloud Console UI, and the prefix for the default +// hostname of the instance as defined by the [default internal DNS name]. +// +// [default internal DNS name]: https://cloud.google.com/compute/docs/internal-dns#instance-fully-qualified-domain-names +func GCPGCEInstanceName(val string) attribute.KeyValue { + return GCPGCEInstanceNameKey.String(val) +} + +// Enum values for gcp.apphub.service.criticality_type +var ( + // Mission critical service. + // Stability: development + GCPAppHubServiceCriticalityTypeMissionCritical = GCPAppHubServiceCriticalityTypeKey.String("MISSION_CRITICAL") + // High impact. + // Stability: development + GCPAppHubServiceCriticalityTypeHigh = GCPAppHubServiceCriticalityTypeKey.String("HIGH") + // Medium impact. + // Stability: development + GCPAppHubServiceCriticalityTypeMedium = GCPAppHubServiceCriticalityTypeKey.String("MEDIUM") + // Low impact. + // Stability: development + GCPAppHubServiceCriticalityTypeLow = GCPAppHubServiceCriticalityTypeKey.String("LOW") +) + +// Enum values for gcp.apphub.service.environment_type +var ( + // Production environment. + // Stability: development + GCPAppHubServiceEnvironmentTypeProduction = GCPAppHubServiceEnvironmentTypeKey.String("PRODUCTION") + // Staging environment. + // Stability: development + GCPAppHubServiceEnvironmentTypeStaging = GCPAppHubServiceEnvironmentTypeKey.String("STAGING") + // Test environment. + // Stability: development + GCPAppHubServiceEnvironmentTypeTest = GCPAppHubServiceEnvironmentTypeKey.String("TEST") + // Development environment. + // Stability: development + GCPAppHubServiceEnvironmentTypeDevelopment = GCPAppHubServiceEnvironmentTypeKey.String("DEVELOPMENT") +) + +// Enum values for gcp.apphub.workload.criticality_type +var ( + // Mission critical service. + // Stability: development + GCPAppHubWorkloadCriticalityTypeMissionCritical = GCPAppHubWorkloadCriticalityTypeKey.String("MISSION_CRITICAL") + // High impact. + // Stability: development + GCPAppHubWorkloadCriticalityTypeHigh = GCPAppHubWorkloadCriticalityTypeKey.String("HIGH") + // Medium impact. + // Stability: development + GCPAppHubWorkloadCriticalityTypeMedium = GCPAppHubWorkloadCriticalityTypeKey.String("MEDIUM") + // Low impact. + // Stability: development + GCPAppHubWorkloadCriticalityTypeLow = GCPAppHubWorkloadCriticalityTypeKey.String("LOW") +) + +// Enum values for gcp.apphub.workload.environment_type +var ( + // Production environment. + // Stability: development + GCPAppHubWorkloadEnvironmentTypeProduction = GCPAppHubWorkloadEnvironmentTypeKey.String("PRODUCTION") + // Staging environment. + // Stability: development + GCPAppHubWorkloadEnvironmentTypeStaging = GCPAppHubWorkloadEnvironmentTypeKey.String("STAGING") + // Test environment. + // Stability: development + GCPAppHubWorkloadEnvironmentTypeTest = GCPAppHubWorkloadEnvironmentTypeKey.String("TEST") + // Development environment. + // Stability: development + GCPAppHubWorkloadEnvironmentTypeDevelopment = GCPAppHubWorkloadEnvironmentTypeKey.String("DEVELOPMENT") +) + +// Enum values for gcp.apphub_destination.service.criticality_type +var ( + // Mission critical service. + // Stability: development + GCPAppHubDestinationServiceCriticalityTypeMissionCritical = GCPAppHubDestinationServiceCriticalityTypeKey.String("MISSION_CRITICAL") + // High impact. + // Stability: development + GCPAppHubDestinationServiceCriticalityTypeHigh = GCPAppHubDestinationServiceCriticalityTypeKey.String("HIGH") + // Medium impact. + // Stability: development + GCPAppHubDestinationServiceCriticalityTypeMedium = GCPAppHubDestinationServiceCriticalityTypeKey.String("MEDIUM") + // Low impact. + // Stability: development + GCPAppHubDestinationServiceCriticalityTypeLow = GCPAppHubDestinationServiceCriticalityTypeKey.String("LOW") +) + +// Enum values for gcp.apphub_destination.service.environment_type +var ( + // Production environment. + // Stability: development + GCPAppHubDestinationServiceEnvironmentTypeProduction = GCPAppHubDestinationServiceEnvironmentTypeKey.String("PRODUCTION") + // Staging environment. + // Stability: development + GCPAppHubDestinationServiceEnvironmentTypeStaging = GCPAppHubDestinationServiceEnvironmentTypeKey.String("STAGING") + // Test environment. + // Stability: development + GCPAppHubDestinationServiceEnvironmentTypeTest = GCPAppHubDestinationServiceEnvironmentTypeKey.String("TEST") + // Development environment. + // Stability: development + GCPAppHubDestinationServiceEnvironmentTypeDevelopment = GCPAppHubDestinationServiceEnvironmentTypeKey.String("DEVELOPMENT") +) + +// Enum values for gcp.apphub_destination.workload.criticality_type +var ( + // Mission critical service. + // Stability: development + GCPAppHubDestinationWorkloadCriticalityTypeMissionCritical = GCPAppHubDestinationWorkloadCriticalityTypeKey.String("MISSION_CRITICAL") + // High impact. + // Stability: development + GCPAppHubDestinationWorkloadCriticalityTypeHigh = GCPAppHubDestinationWorkloadCriticalityTypeKey.String("HIGH") + // Medium impact. + // Stability: development + GCPAppHubDestinationWorkloadCriticalityTypeMedium = GCPAppHubDestinationWorkloadCriticalityTypeKey.String("MEDIUM") + // Low impact. + // Stability: development + GCPAppHubDestinationWorkloadCriticalityTypeLow = GCPAppHubDestinationWorkloadCriticalityTypeKey.String("LOW") +) + +// Enum values for gcp.apphub_destination.workload.environment_type +var ( + // Production environment. + // Stability: development + GCPAppHubDestinationWorkloadEnvironmentTypeProduction = GCPAppHubDestinationWorkloadEnvironmentTypeKey.String("PRODUCTION") + // Staging environment. + // Stability: development + GCPAppHubDestinationWorkloadEnvironmentTypeStaging = GCPAppHubDestinationWorkloadEnvironmentTypeKey.String("STAGING") + // Test environment. + // Stability: development + GCPAppHubDestinationWorkloadEnvironmentTypeTest = GCPAppHubDestinationWorkloadEnvironmentTypeKey.String("TEST") + // Development environment. + // Stability: development + GCPAppHubDestinationWorkloadEnvironmentTypeDevelopment = GCPAppHubDestinationWorkloadEnvironmentTypeKey.String("DEVELOPMENT") +) + +// Namespace: gen_ai +const ( + // GenAIAgentDescriptionKey is the attribute Key conforming to the + // "gen_ai.agent.description" semantic conventions. It represents the free-form + // description of the GenAI agent provided by the application. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Helps with math problems", "Generates fiction stories" + GenAIAgentDescriptionKey = attribute.Key("gen_ai.agent.description") + + // GenAIAgentIDKey is the attribute Key conforming to the "gen_ai.agent.id" + // semantic conventions. It represents the unique identifier of the GenAI agent. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "asst_5j66UpCpwteGg4YSxUnt7lPY" + GenAIAgentIDKey = attribute.Key("gen_ai.agent.id") + + // GenAIAgentNameKey is the attribute Key conforming to the "gen_ai.agent.name" + // semantic conventions. It represents the human-readable name of the GenAI + // agent provided by the application. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Math Tutor", "Fiction Writer" + GenAIAgentNameKey = attribute.Key("gen_ai.agent.name") + + // GenAIConversationIDKey is the attribute Key conforming to the + // "gen_ai.conversation.id" semantic conventions. It represents the unique + // identifier for a conversation (session, thread), used to store and correlate + // messages within this conversation. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "conv_5j66UpCpwteGg4YSxUnt7lPY" + GenAIConversationIDKey = attribute.Key("gen_ai.conversation.id") + + // GenAIDataSourceIDKey is the attribute Key conforming to the + // "gen_ai.data_source.id" semantic conventions. It represents the data source + // identifier. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "H7STPQYOND" + // Note: Data sources are used by AI agents and RAG applications to store + // grounding data. A data source may be an external database, object store, + // document collection, website, or any other storage system used by the GenAI + // agent or application. The `gen_ai.data_source.id` SHOULD match the identifier + // used by the GenAI system rather than a name specific to the external storage, + // such as a database or object store. Semantic conventions referencing + // `gen_ai.data_source.id` MAY also leverage additional attributes, such as + // `db.*`, to further identify and describe the data source. + GenAIDataSourceIDKey = attribute.Key("gen_ai.data_source.id") + + // GenAIEmbeddingsDimensionCountKey is the attribute Key conforming to the + // "gen_ai.embeddings.dimension.count" semantic conventions. It represents the + // number of dimensions the resulting output embeddings should have. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 512, 1024 + GenAIEmbeddingsDimensionCountKey = attribute.Key("gen_ai.embeddings.dimension.count") + + // GenAIEvaluationExplanationKey is the attribute Key conforming to the + // "gen_ai.evaluation.explanation" semantic conventions. It represents a + // free-form explanation for the assigned score provided by the evaluator. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "The response is factually accurate but lacks sufficient detail to + // fully address the question." + GenAIEvaluationExplanationKey = attribute.Key("gen_ai.evaluation.explanation") + + // GenAIEvaluationNameKey is the attribute Key conforming to the + // "gen_ai.evaluation.name" semantic conventions. It represents the name of the + // evaluation metric used for the GenAI response. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Relevance", "IntentResolution" + GenAIEvaluationNameKey = attribute.Key("gen_ai.evaluation.name") + + // GenAIEvaluationScoreLabelKey is the attribute Key conforming to the + // "gen_ai.evaluation.score.label" semantic conventions. It represents the human + // readable label for evaluation. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "relevant", "not_relevant", "correct", "incorrect", "pass", "fail" + // Note: This attribute provides a human-readable interpretation of the + // evaluation score produced by an evaluator. For example, a score value of 1 + // could mean "relevant" in one evaluation system and "not relevant" in another, + // depending on the scoring range and evaluator. The label SHOULD have low + // cardinality. Possible values depend on the evaluation metric and evaluator + // used; implementations SHOULD document the possible values. + GenAIEvaluationScoreLabelKey = attribute.Key("gen_ai.evaluation.score.label") + + // GenAIEvaluationScoreValueKey is the attribute Key conforming to the + // "gen_ai.evaluation.score.value" semantic conventions. It represents the + // evaluation score returned by the evaluator. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 4.0 + GenAIEvaluationScoreValueKey = attribute.Key("gen_ai.evaluation.score.value") + + // GenAIInputMessagesKey is the attribute Key conforming to the + // "gen_ai.input.messages" semantic conventions. It represents the chat history + // provided to the model as an input. + // + // Type: any + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "[\n {\n "role": "user",\n "parts": [\n {\n "type": "text",\n + // "content": "Weather in Paris?"\n }\n ]\n },\n {\n "role": "assistant",\n + // "parts": [\n {\n "type": "tool_call",\n "id": + // "call_VSPygqKTWdrhaFErNvMV18Yl",\n "name": "get_weather",\n "arguments": {\n + // "location": "Paris"\n }\n }\n ]\n },\n {\n "role": "tool",\n "parts": [\n {\n + // "type": "tool_call_response",\n "id": " call_VSPygqKTWdrhaFErNvMV18Yl",\n + // "result": "rainy, 57°F"\n }\n ]\n }\n]\n" + // Note: Instrumentations MUST follow [Input messages JSON schema]. + // When the attribute is recorded on events, it MUST be recorded in structured + // form. When recorded on spans, it MAY be recorded as a JSON string if + // structured + // format is not supported and SHOULD be recorded in structured form otherwise. + // + // Messages MUST be provided in the order they were sent to the model. + // Instrumentations MAY provide a way for users to filter or truncate + // input messages. + // + // > [!Warning] + // > This attribute is likely to contain sensitive information including + // > user/PII data. + // + // See [Recording content on attributes] + // section for more details. + // + // [Input messages JSON schema]: /docs/gen-ai/gen-ai-input-messages.json + // [Recording content on attributes]: /docs/gen-ai/gen-ai-spans.md#recording-content-on-attributes + GenAIInputMessagesKey = attribute.Key("gen_ai.input.messages") + + // GenAIOperationNameKey is the attribute Key conforming to the + // "gen_ai.operation.name" semantic conventions. It represents the name of the + // operation being performed. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: If one of the predefined values applies, but specific system uses a + // different name it's RECOMMENDED to document it in the semantic conventions + // for specific GenAI system and use system-specific name in the + // instrumentation. If a different name is not documented, instrumentation + // libraries SHOULD use applicable predefined value. + GenAIOperationNameKey = attribute.Key("gen_ai.operation.name") + + // GenAIOutputMessagesKey is the attribute Key conforming to the + // "gen_ai.output.messages" semantic conventions. It represents the messages + // returned by the model where each message represents a specific model response + // (choice, candidate). + // + // Type: any + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "[\n {\n "role": "assistant",\n "parts": [\n {\n "type": "text",\n + // "content": "The weather in Paris is currently rainy with a temperature of + // 57°F."\n }\n ],\n "finish_reason": "stop"\n }\n]\n" + // Note: Instrumentations MUST follow [Output messages JSON schema] + // + // Each message represents a single output choice/candidate generated by + // the model. Each message corresponds to exactly one generation + // (choice/candidate) and vice versa - one choice cannot be split across + // multiple messages or one message cannot contain parts from multiple choices. + // + // When the attribute is recorded on events, it MUST be recorded in structured + // form. When recorded on spans, it MAY be recorded as a JSON string if + // structured + // format is not supported and SHOULD be recorded in structured form otherwise. + // + // Instrumentations MAY provide a way for users to filter or truncate + // output messages. + // + // > [!Warning] + // > This attribute is likely to contain sensitive information including + // > user/PII data. + // + // See [Recording content on attributes] + // section for more details. + // + // [Output messages JSON schema]: /docs/gen-ai/gen-ai-output-messages.json + // [Recording content on attributes]: /docs/gen-ai/gen-ai-spans.md#recording-content-on-attributes + GenAIOutputMessagesKey = attribute.Key("gen_ai.output.messages") + + // GenAIOutputTypeKey is the attribute Key conforming to the + // "gen_ai.output.type" semantic conventions. It represents the represents the + // content type requested by the client. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: This attribute SHOULD be used when the client requests output of a + // specific type. The model may return zero or more outputs of this type. + // This attribute specifies the output modality and not the actual output + // format. For example, if an image is requested, the actual output could be a + // URL pointing to an image file. + // Additional output format details may be recorded in the future in the + // `gen_ai.output.{type}.*` attributes. + GenAIOutputTypeKey = attribute.Key("gen_ai.output.type") + + // GenAIPromptNameKey is the attribute Key conforming to the + // "gen_ai.prompt.name" semantic conventions. It represents the name of the + // prompt that uniquely identifies it. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "analyze-code" + GenAIPromptNameKey = attribute.Key("gen_ai.prompt.name") + + // GenAIProviderNameKey is the attribute Key conforming to the + // "gen_ai.provider.name" semantic conventions. It represents the Generative AI + // provider as identified by the client or server instrumentation. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: The attribute SHOULD be set based on the instrumentation's best + // knowledge and may differ from the actual model provider. + // + // Multiple providers, including Azure OpenAI, Gemini, and AI hosting platforms + // are accessible using the OpenAI REST API and corresponding client libraries, + // but may proxy or host models from different providers. + // + // The `gen_ai.request.model`, `gen_ai.response.model`, and `server.address` + // attributes may help identify the actual system in use. + // + // The `gen_ai.provider.name` attribute acts as a discriminator that + // identifies the GenAI telemetry format flavor specific to that provider + // within GenAI semantic conventions. + // It SHOULD be set consistently with provider-specific attributes and signals. + // For example, GenAI spans, metrics, and events related to AWS Bedrock + // should have the `gen_ai.provider.name` set to `aws.bedrock` and include + // applicable `aws.bedrock.*` attributes and are not expected to include + // `openai.*` attributes. + GenAIProviderNameKey = attribute.Key("gen_ai.provider.name") + + // GenAIRequestChoiceCountKey is the attribute Key conforming to the + // "gen_ai.request.choice.count" semantic conventions. It represents the target + // number of candidate completions to return. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 3 + GenAIRequestChoiceCountKey = attribute.Key("gen_ai.request.choice.count") + + // GenAIRequestEncodingFormatsKey is the attribute Key conforming to the + // "gen_ai.request.encoding_formats" semantic conventions. It represents the + // encoding formats requested in an embeddings operation, if specified. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "base64"], ["float", "binary" + // Note: In some GenAI systems the encoding formats are called embedding types. + // Also, some GenAI systems only accept a single format per request. + GenAIRequestEncodingFormatsKey = attribute.Key("gen_ai.request.encoding_formats") + + // GenAIRequestFrequencyPenaltyKey is the attribute Key conforming to the + // "gen_ai.request.frequency_penalty" semantic conventions. It represents the + // frequency penalty setting for the GenAI request. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 0.1 + GenAIRequestFrequencyPenaltyKey = attribute.Key("gen_ai.request.frequency_penalty") + + // GenAIRequestMaxTokensKey is the attribute Key conforming to the + // "gen_ai.request.max_tokens" semantic conventions. It represents the maximum + // number of tokens the model generates for a request. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 100 + GenAIRequestMaxTokensKey = attribute.Key("gen_ai.request.max_tokens") + + // GenAIRequestModelKey is the attribute Key conforming to the + // "gen_ai.request.model" semantic conventions. It represents the name of the + // GenAI model a request is being made to. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: gpt-4 + GenAIRequestModelKey = attribute.Key("gen_ai.request.model") + + // GenAIRequestPresencePenaltyKey is the attribute Key conforming to the + // "gen_ai.request.presence_penalty" semantic conventions. It represents the + // presence penalty setting for the GenAI request. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 0.1 + GenAIRequestPresencePenaltyKey = attribute.Key("gen_ai.request.presence_penalty") + + // GenAIRequestSeedKey is the attribute Key conforming to the + // "gen_ai.request.seed" semantic conventions. It represents the requests with + // same seed value more likely to return same result. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 100 + GenAIRequestSeedKey = attribute.Key("gen_ai.request.seed") + + // GenAIRequestStopSequencesKey is the attribute Key conforming to the + // "gen_ai.request.stop_sequences" semantic conventions. It represents the list + // of sequences that the model will use to stop generating further tokens. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "forest", "lived" + GenAIRequestStopSequencesKey = attribute.Key("gen_ai.request.stop_sequences") + + // GenAIRequestTemperatureKey is the attribute Key conforming to the + // "gen_ai.request.temperature" semantic conventions. It represents the + // temperature setting for the GenAI request. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 0.0 + GenAIRequestTemperatureKey = attribute.Key("gen_ai.request.temperature") + + // GenAIRequestTopKKey is the attribute Key conforming to the + // "gen_ai.request.top_k" semantic conventions. It represents the top_k sampling + // setting for the GenAI request. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1.0 + GenAIRequestTopKKey = attribute.Key("gen_ai.request.top_k") + + // GenAIRequestTopPKey is the attribute Key conforming to the + // "gen_ai.request.top_p" semantic conventions. It represents the top_p sampling + // setting for the GenAI request. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1.0 + GenAIRequestTopPKey = attribute.Key("gen_ai.request.top_p") + + // GenAIResponseFinishReasonsKey is the attribute Key conforming to the + // "gen_ai.response.finish_reasons" semantic conventions. It represents the + // array of reasons the model stopped generating tokens, corresponding to each + // generation received. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "stop"], ["stop", "length" + GenAIResponseFinishReasonsKey = attribute.Key("gen_ai.response.finish_reasons") + + // GenAIResponseIDKey is the attribute Key conforming to the + // "gen_ai.response.id" semantic conventions. It represents the unique + // identifier for the completion. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "chatcmpl-123" + GenAIResponseIDKey = attribute.Key("gen_ai.response.id") + + // GenAIResponseModelKey is the attribute Key conforming to the + // "gen_ai.response.model" semantic conventions. It represents the name of the + // model that generated the response. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "gpt-4-0613" + GenAIResponseModelKey = attribute.Key("gen_ai.response.model") + + // GenAISystemInstructionsKey is the attribute Key conforming to the + // "gen_ai.system_instructions" semantic conventions. It represents the system + // message or instructions provided to the GenAI model separately from the chat + // history. + // + // Type: any + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "[\n {\n "type": "text",\n "content": "You are an Agent that greet + // users, always use greetings tool to respond"\n }\n]\n", "[\n {\n "type": + // "text",\n "content": "You are a language translator."\n },\n {\n "type": + // "text",\n "content": "Your mission is to translate text in English to + // French."\n }\n]\n" + // Note: This attribute SHOULD be used when the corresponding provider or API + // allows to provide system instructions or messages separately from the + // chat history. + // + // Instructions that are part of the chat history SHOULD be recorded in + // `gen_ai.input.messages` attribute instead. + // + // Instrumentations MUST follow [System instructions JSON schema]. + // + // When recorded on spans, it MAY be recorded as a JSON string if structured + // format is not supported and SHOULD be recorded in structured form otherwise. + // + // Instrumentations MAY provide a way for users to filter or truncate + // system instructions. + // + // > [!Warning] + // > This attribute may contain sensitive information. + // + // See [Recording content on attributes] + // section for more details. + // + // [System instructions JSON schema]: /docs/gen-ai/gen-ai-system-instructions.json + // [Recording content on attributes]: /docs/gen-ai/gen-ai-spans.md#recording-content-on-attributes + GenAISystemInstructionsKey = attribute.Key("gen_ai.system_instructions") + + // GenAITokenTypeKey is the attribute Key conforming to the "gen_ai.token.type" + // semantic conventions. It represents the type of token being counted. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "input", "output" + GenAITokenTypeKey = attribute.Key("gen_ai.token.type") + + // GenAIToolCallArgumentsKey is the attribute Key conforming to the + // "gen_ai.tool.call.arguments" semantic conventions. It represents the + // parameters passed to the tool call. + // + // Type: any + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "{\n "location": "San Francisco?",\n "date": "2025-10-01"\n}\n" + // Note: > [!WARNING] + // + // > This attribute may contain sensitive information. + // + // It's expected to be an object - in case a serialized string is available + // to the instrumentation, the instrumentation SHOULD do the best effort to + // deserialize it to an object. When recorded on spans, it MAY be recorded as a + // JSON string if structured format is not supported and SHOULD be recorded in + // structured form otherwise. + GenAIToolCallArgumentsKey = attribute.Key("gen_ai.tool.call.arguments") + + // GenAIToolCallIDKey is the attribute Key conforming to the + // "gen_ai.tool.call.id" semantic conventions. It represents the tool call + // identifier. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "call_mszuSIzqtI65i1wAUOE8w5H4" + GenAIToolCallIDKey = attribute.Key("gen_ai.tool.call.id") + + // GenAIToolCallResultKey is the attribute Key conforming to the + // "gen_ai.tool.call.result" semantic conventions. It represents the result + // returned by the tool call (if any and if execution was successful). + // + // Type: any + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "{\n "temperature_range": {\n "high": 75,\n "low": 60\n },\n + // "conditions": "sunny"\n}\n" + // Note: > [!WARNING] + // + // > This attribute may contain sensitive information. + // + // It's expected to be an object - in case a serialized string is available + // to the instrumentation, the instrumentation SHOULD do the best effort to + // deserialize it to an object. When recorded on spans, it MAY be recorded as a + // JSON string if structured format is not supported and SHOULD be recorded in + // structured form otherwise. + GenAIToolCallResultKey = attribute.Key("gen_ai.tool.call.result") + + // GenAIToolDefinitionsKey is the attribute Key conforming to the + // "gen_ai.tool.definitions" semantic conventions. It represents the list of + // source system tool definitions available to the GenAI agent or model. + // + // Type: any + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "[\n {\n "type": "function",\n "name": "get_current_weather",\n + // "description": "Get the current weather in a given location",\n "parameters": + // {\n "type": "object",\n "properties": {\n "location": {\n "type": "string",\n + // "description": "The city and state, e.g. San Francisco, CA"\n },\n "unit": + // {\n "type": "string",\n "enum": [\n "celsius",\n "fahrenheit"\n ]\n }\n },\n + // "required": [\n "location",\n "unit"\n ]\n }\n }\n]\n" + // Note: The value of this attribute matches source system tool definition + // format. + // + // It's expected to be an array of objects where each object represents a tool + // definition. In case a serialized string is available + // to the instrumentation, the instrumentation SHOULD do the best effort to + // deserialize it to an array. When recorded on spans, it MAY be recorded as a + // JSON string if structured format is not supported and SHOULD be recorded in + // structured form otherwise. + // + // Since this attribute could be large, it's NOT RECOMMENDED to populate + // it by default. Instrumentations MAY provide a way to enable + // populating this attribute. + GenAIToolDefinitionsKey = attribute.Key("gen_ai.tool.definitions") + + // GenAIToolDescriptionKey is the attribute Key conforming to the + // "gen_ai.tool.description" semantic conventions. It represents the tool + // description. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Multiply two numbers" + GenAIToolDescriptionKey = attribute.Key("gen_ai.tool.description") + + // GenAIToolNameKey is the attribute Key conforming to the "gen_ai.tool.name" + // semantic conventions. It represents the name of the tool utilized by the + // agent. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Flights" + GenAIToolNameKey = attribute.Key("gen_ai.tool.name") + + // GenAIToolTypeKey is the attribute Key conforming to the "gen_ai.tool.type" + // semantic conventions. It represents the type of the tool utilized by the + // agent. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "function", "extension", "datastore" + // Note: Extension: A tool executed on the agent-side to directly call external + // APIs, bridging the gap between the agent and real-world systems. + // Agent-side operations involve actions that are performed by the agent on the + // server or within the agent's controlled environment. + // Function: A tool executed on the client-side, where the agent generates + // parameters for a predefined function, and the client executes the logic. + // Client-side operations are actions taken on the user's end or within the + // client application. + // Datastore: A tool used by the agent to access and query structured or + // unstructured external data for retrieval-augmented tasks or knowledge + // updates. + GenAIToolTypeKey = attribute.Key("gen_ai.tool.type") + + // GenAIUsageInputTokensKey is the attribute Key conforming to the + // "gen_ai.usage.input_tokens" semantic conventions. It represents the number of + // tokens used in the GenAI input (prompt). + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 100 + GenAIUsageInputTokensKey = attribute.Key("gen_ai.usage.input_tokens") + + // GenAIUsageOutputTokensKey is the attribute Key conforming to the + // "gen_ai.usage.output_tokens" semantic conventions. It represents the number + // of tokens used in the GenAI response (completion). + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 180 + GenAIUsageOutputTokensKey = attribute.Key("gen_ai.usage.output_tokens") +) + +// GenAIAgentDescription returns an attribute KeyValue conforming to the +// "gen_ai.agent.description" semantic conventions. It represents the free-form +// description of the GenAI agent provided by the application. +func GenAIAgentDescription(val string) attribute.KeyValue { + return GenAIAgentDescriptionKey.String(val) +} + +// GenAIAgentID returns an attribute KeyValue conforming to the "gen_ai.agent.id" +// semantic conventions. It represents the unique identifier of the GenAI agent. +func GenAIAgentID(val string) attribute.KeyValue { + return GenAIAgentIDKey.String(val) +} + +// GenAIAgentName returns an attribute KeyValue conforming to the +// "gen_ai.agent.name" semantic conventions. It represents the human-readable +// name of the GenAI agent provided by the application. +func GenAIAgentName(val string) attribute.KeyValue { + return GenAIAgentNameKey.String(val) +} + +// GenAIConversationID returns an attribute KeyValue conforming to the +// "gen_ai.conversation.id" semantic conventions. It represents the unique +// identifier for a conversation (session, thread), used to store and correlate +// messages within this conversation. +func GenAIConversationID(val string) attribute.KeyValue { + return GenAIConversationIDKey.String(val) +} + +// GenAIDataSourceID returns an attribute KeyValue conforming to the +// "gen_ai.data_source.id" semantic conventions. It represents the data source +// identifier. +func GenAIDataSourceID(val string) attribute.KeyValue { + return GenAIDataSourceIDKey.String(val) +} + +// GenAIEmbeddingsDimensionCount returns an attribute KeyValue conforming to the +// "gen_ai.embeddings.dimension.count" semantic conventions. It represents the +// number of dimensions the resulting output embeddings should have. +func GenAIEmbeddingsDimensionCount(val int) attribute.KeyValue { + return GenAIEmbeddingsDimensionCountKey.Int(val) +} + +// GenAIEvaluationExplanation returns an attribute KeyValue conforming to the +// "gen_ai.evaluation.explanation" semantic conventions. It represents a +// free-form explanation for the assigned score provided by the evaluator. +func GenAIEvaluationExplanation(val string) attribute.KeyValue { + return GenAIEvaluationExplanationKey.String(val) +} + +// GenAIEvaluationName returns an attribute KeyValue conforming to the +// "gen_ai.evaluation.name" semantic conventions. It represents the name of the +// evaluation metric used for the GenAI response. +func GenAIEvaluationName(val string) attribute.KeyValue { + return GenAIEvaluationNameKey.String(val) +} + +// GenAIEvaluationScoreLabel returns an attribute KeyValue conforming to the +// "gen_ai.evaluation.score.label" semantic conventions. It represents the human +// readable label for evaluation. +func GenAIEvaluationScoreLabel(val string) attribute.KeyValue { + return GenAIEvaluationScoreLabelKey.String(val) +} + +// GenAIEvaluationScoreValue returns an attribute KeyValue conforming to the +// "gen_ai.evaluation.score.value" semantic conventions. It represents the +// evaluation score returned by the evaluator. +func GenAIEvaluationScoreValue(val float64) attribute.KeyValue { + return GenAIEvaluationScoreValueKey.Float64(val) +} + +// GenAIPromptName returns an attribute KeyValue conforming to the +// "gen_ai.prompt.name" semantic conventions. It represents the name of the +// prompt that uniquely identifies it. +func GenAIPromptName(val string) attribute.KeyValue { + return GenAIPromptNameKey.String(val) +} + +// GenAIRequestChoiceCount returns an attribute KeyValue conforming to the +// "gen_ai.request.choice.count" semantic conventions. It represents the target +// number of candidate completions to return. +func GenAIRequestChoiceCount(val int) attribute.KeyValue { + return GenAIRequestChoiceCountKey.Int(val) +} + +// GenAIRequestEncodingFormats returns an attribute KeyValue conforming to the +// "gen_ai.request.encoding_formats" semantic conventions. It represents the +// encoding formats requested in an embeddings operation, if specified. +func GenAIRequestEncodingFormats(val ...string) attribute.KeyValue { + return GenAIRequestEncodingFormatsKey.StringSlice(val) +} + +// GenAIRequestFrequencyPenalty returns an attribute KeyValue conforming to the +// "gen_ai.request.frequency_penalty" semantic conventions. It represents the +// frequency penalty setting for the GenAI request. +func GenAIRequestFrequencyPenalty(val float64) attribute.KeyValue { + return GenAIRequestFrequencyPenaltyKey.Float64(val) +} + +// GenAIRequestMaxTokens returns an attribute KeyValue conforming to the +// "gen_ai.request.max_tokens" semantic conventions. It represents the maximum +// number of tokens the model generates for a request. +func GenAIRequestMaxTokens(val int) attribute.KeyValue { + return GenAIRequestMaxTokensKey.Int(val) +} + +// GenAIRequestModel returns an attribute KeyValue conforming to the +// "gen_ai.request.model" semantic conventions. It represents the name of the +// GenAI model a request is being made to. +func GenAIRequestModel(val string) attribute.KeyValue { + return GenAIRequestModelKey.String(val) +} + +// GenAIRequestPresencePenalty returns an attribute KeyValue conforming to the +// "gen_ai.request.presence_penalty" semantic conventions. It represents the +// presence penalty setting for the GenAI request. +func GenAIRequestPresencePenalty(val float64) attribute.KeyValue { + return GenAIRequestPresencePenaltyKey.Float64(val) +} + +// GenAIRequestSeed returns an attribute KeyValue conforming to the +// "gen_ai.request.seed" semantic conventions. It represents the requests with +// same seed value more likely to return same result. +func GenAIRequestSeed(val int) attribute.KeyValue { + return GenAIRequestSeedKey.Int(val) +} + +// GenAIRequestStopSequences returns an attribute KeyValue conforming to the +// "gen_ai.request.stop_sequences" semantic conventions. It represents the list +// of sequences that the model will use to stop generating further tokens. +func GenAIRequestStopSequences(val ...string) attribute.KeyValue { + return GenAIRequestStopSequencesKey.StringSlice(val) +} + +// GenAIRequestTemperature returns an attribute KeyValue conforming to the +// "gen_ai.request.temperature" semantic conventions. It represents the +// temperature setting for the GenAI request. +func GenAIRequestTemperature(val float64) attribute.KeyValue { + return GenAIRequestTemperatureKey.Float64(val) +} + +// GenAIRequestTopK returns an attribute KeyValue conforming to the +// "gen_ai.request.top_k" semantic conventions. It represents the top_k sampling +// setting for the GenAI request. +func GenAIRequestTopK(val float64) attribute.KeyValue { + return GenAIRequestTopKKey.Float64(val) +} + +// GenAIRequestTopP returns an attribute KeyValue conforming to the +// "gen_ai.request.top_p" semantic conventions. It represents the top_p sampling +// setting for the GenAI request. +func GenAIRequestTopP(val float64) attribute.KeyValue { + return GenAIRequestTopPKey.Float64(val) +} + +// GenAIResponseFinishReasons returns an attribute KeyValue conforming to the +// "gen_ai.response.finish_reasons" semantic conventions. It represents the array +// of reasons the model stopped generating tokens, corresponding to each +// generation received. +func GenAIResponseFinishReasons(val ...string) attribute.KeyValue { + return GenAIResponseFinishReasonsKey.StringSlice(val) +} + +// GenAIResponseID returns an attribute KeyValue conforming to the +// "gen_ai.response.id" semantic conventions. It represents the unique identifier +// for the completion. +func GenAIResponseID(val string) attribute.KeyValue { + return GenAIResponseIDKey.String(val) +} + +// GenAIResponseModel returns an attribute KeyValue conforming to the +// "gen_ai.response.model" semantic conventions. It represents the name of the +// model that generated the response. +func GenAIResponseModel(val string) attribute.KeyValue { + return GenAIResponseModelKey.String(val) +} + +// GenAIToolCallID returns an attribute KeyValue conforming to the +// "gen_ai.tool.call.id" semantic conventions. It represents the tool call +// identifier. +func GenAIToolCallID(val string) attribute.KeyValue { + return GenAIToolCallIDKey.String(val) +} + +// GenAIToolDescription returns an attribute KeyValue conforming to the +// "gen_ai.tool.description" semantic conventions. It represents the tool +// description. +func GenAIToolDescription(val string) attribute.KeyValue { + return GenAIToolDescriptionKey.String(val) +} + +// GenAIToolName returns an attribute KeyValue conforming to the +// "gen_ai.tool.name" semantic conventions. It represents the name of the tool +// utilized by the agent. +func GenAIToolName(val string) attribute.KeyValue { + return GenAIToolNameKey.String(val) +} + +// GenAIToolType returns an attribute KeyValue conforming to the +// "gen_ai.tool.type" semantic conventions. It represents the type of the tool +// utilized by the agent. +func GenAIToolType(val string) attribute.KeyValue { + return GenAIToolTypeKey.String(val) +} + +// GenAIUsageInputTokens returns an attribute KeyValue conforming to the +// "gen_ai.usage.input_tokens" semantic conventions. It represents the number of +// tokens used in the GenAI input (prompt). +func GenAIUsageInputTokens(val int) attribute.KeyValue { + return GenAIUsageInputTokensKey.Int(val) +} + +// GenAIUsageOutputTokens returns an attribute KeyValue conforming to the +// "gen_ai.usage.output_tokens" semantic conventions. It represents the number of +// tokens used in the GenAI response (completion). +func GenAIUsageOutputTokens(val int) attribute.KeyValue { + return GenAIUsageOutputTokensKey.Int(val) +} + +// Enum values for gen_ai.operation.name +var ( + // Chat completion operation such as [OpenAI Chat API] + // Stability: development + // + // [OpenAI Chat API]: https://platform.openai.com/docs/api-reference/chat + GenAIOperationNameChat = GenAIOperationNameKey.String("chat") + // Multimodal content generation operation such as [Gemini Generate Content] + // Stability: development + // + // [Gemini Generate Content]: https://ai.google.dev/api/generate-content + GenAIOperationNameGenerateContent = GenAIOperationNameKey.String("generate_content") + // Text completions operation such as [OpenAI Completions API (Legacy)] + // Stability: development + // + // [OpenAI Completions API (Legacy)]: https://platform.openai.com/docs/api-reference/completions + GenAIOperationNameTextCompletion = GenAIOperationNameKey.String("text_completion") + // Embeddings operation such as [OpenAI Create embeddings API] + // Stability: development + // + // [OpenAI Create embeddings API]: https://platform.openai.com/docs/api-reference/embeddings/create + GenAIOperationNameEmbeddings = GenAIOperationNameKey.String("embeddings") + // Create GenAI agent + // Stability: development + GenAIOperationNameCreateAgent = GenAIOperationNameKey.String("create_agent") + // Invoke GenAI agent + // Stability: development + GenAIOperationNameInvokeAgent = GenAIOperationNameKey.String("invoke_agent") + // Execute a tool + // Stability: development + GenAIOperationNameExecuteTool = GenAIOperationNameKey.String("execute_tool") +) + +// Enum values for gen_ai.output.type +var ( + // Plain text + // Stability: development + GenAIOutputTypeText = GenAIOutputTypeKey.String("text") + // JSON object with known or unknown schema + // Stability: development + GenAIOutputTypeJSON = GenAIOutputTypeKey.String("json") + // Image + // Stability: development + GenAIOutputTypeImage = GenAIOutputTypeKey.String("image") + // Speech + // Stability: development + GenAIOutputTypeSpeech = GenAIOutputTypeKey.String("speech") +) + +// Enum values for gen_ai.provider.name +var ( + // [OpenAI] + // Stability: development + // + // [OpenAI]: https://openai.com/ + GenAIProviderNameOpenAI = GenAIProviderNameKey.String("openai") + // Any Google generative AI endpoint + // Stability: development + GenAIProviderNameGCPGenAI = GenAIProviderNameKey.String("gcp.gen_ai") + // [Vertex AI] + // Stability: development + // + // [Vertex AI]: https://cloud.google.com/vertex-ai + GenAIProviderNameGCPVertexAI = GenAIProviderNameKey.String("gcp.vertex_ai") + // [Gemini] + // Stability: development + // + // [Gemini]: https://cloud.google.com/products/gemini + GenAIProviderNameGCPGemini = GenAIProviderNameKey.String("gcp.gemini") + // [Anthropic] + // Stability: development + // + // [Anthropic]: https://www.anthropic.com/ + GenAIProviderNameAnthropic = GenAIProviderNameKey.String("anthropic") + // [Cohere] + // Stability: development + // + // [Cohere]: https://cohere.com/ + GenAIProviderNameCohere = GenAIProviderNameKey.String("cohere") + // Azure AI Inference + // Stability: development + GenAIProviderNameAzureAIInference = GenAIProviderNameKey.String("azure.ai.inference") + // [Azure OpenAI] + // Stability: development + // + // [Azure OpenAI]: https://azure.microsoft.com/products/ai-services/openai-service/ + GenAIProviderNameAzureAIOpenAI = GenAIProviderNameKey.String("azure.ai.openai") + // [IBM Watsonx AI] + // Stability: development + // + // [IBM Watsonx AI]: https://www.ibm.com/products/watsonx-ai + GenAIProviderNameIBMWatsonxAI = GenAIProviderNameKey.String("ibm.watsonx.ai") + // [AWS Bedrock] + // Stability: development + // + // [AWS Bedrock]: https://aws.amazon.com/bedrock + GenAIProviderNameAWSBedrock = GenAIProviderNameKey.String("aws.bedrock") + // [Perplexity] + // Stability: development + // + // [Perplexity]: https://www.perplexity.ai/ + GenAIProviderNamePerplexity = GenAIProviderNameKey.String("perplexity") + // [xAI] + // Stability: development + // + // [xAI]: https://x.ai/ + GenAIProviderNameXAI = GenAIProviderNameKey.String("x_ai") + // [DeepSeek] + // Stability: development + // + // [DeepSeek]: https://www.deepseek.com/ + GenAIProviderNameDeepseek = GenAIProviderNameKey.String("deepseek") + // [Groq] + // Stability: development + // + // [Groq]: https://groq.com/ + GenAIProviderNameGroq = GenAIProviderNameKey.String("groq") + // [Mistral AI] + // Stability: development + // + // [Mistral AI]: https://mistral.ai/ + GenAIProviderNameMistralAI = GenAIProviderNameKey.String("mistral_ai") +) + +// Enum values for gen_ai.token.type +var ( + // Input tokens (prompt, input, etc.) + // Stability: development + GenAITokenTypeInput = GenAITokenTypeKey.String("input") + // Output tokens (completion, response, etc.) + // Stability: development + GenAITokenTypeOutput = GenAITokenTypeKey.String("output") +) + +// Namespace: geo +const ( + // GeoContinentCodeKey is the attribute Key conforming to the + // "geo.continent.code" semantic conventions. It represents the two-letter code + // representing continent’s name. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + GeoContinentCodeKey = attribute.Key("geo.continent.code") + + // GeoCountryISOCodeKey is the attribute Key conforming to the + // "geo.country.iso_code" semantic conventions. It represents the two-letter ISO + // Country Code ([ISO 3166-1 alpha2]). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "CA" + // + // [ISO 3166-1 alpha2]: https://wikipedia.org/wiki/ISO_3166-1#Codes + GeoCountryISOCodeKey = attribute.Key("geo.country.iso_code") + + // GeoLocalityNameKey is the attribute Key conforming to the "geo.locality.name" + // semantic conventions. It represents the locality name. Represents the name of + // a city, town, village, or similar populated place. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Montreal", "Berlin" + GeoLocalityNameKey = attribute.Key("geo.locality.name") + + // GeoLocationLatKey is the attribute Key conforming to the "geo.location.lat" + // semantic conventions. It represents the latitude of the geo location in + // [WGS84]. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 45.505918 + // + // [WGS84]: https://wikipedia.org/wiki/World_Geodetic_System#WGS84 + GeoLocationLatKey = attribute.Key("geo.location.lat") + + // GeoLocationLonKey is the attribute Key conforming to the "geo.location.lon" + // semantic conventions. It represents the longitude of the geo location in + // [WGS84]. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: -73.61483 + // + // [WGS84]: https://wikipedia.org/wiki/World_Geodetic_System#WGS84 + GeoLocationLonKey = attribute.Key("geo.location.lon") + + // GeoPostalCodeKey is the attribute Key conforming to the "geo.postal_code" + // semantic conventions. It represents the postal code associated with the + // location. Values appropriate for this field may also be known as a postcode + // or ZIP code and will vary widely from country to country. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "94040" + GeoPostalCodeKey = attribute.Key("geo.postal_code") + + // GeoRegionISOCodeKey is the attribute Key conforming to the + // "geo.region.iso_code" semantic conventions. It represents the region ISO code + // ([ISO 3166-2]). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "CA-QC" + // + // [ISO 3166-2]: https://wikipedia.org/wiki/ISO_3166-2 + GeoRegionISOCodeKey = attribute.Key("geo.region.iso_code") +) + +// GeoCountryISOCode returns an attribute KeyValue conforming to the +// "geo.country.iso_code" semantic conventions. It represents the two-letter ISO +// Country Code ([ISO 3166-1 alpha2]). +// +// [ISO 3166-1 alpha2]: https://wikipedia.org/wiki/ISO_3166-1#Codes +func GeoCountryISOCode(val string) attribute.KeyValue { + return GeoCountryISOCodeKey.String(val) +} + +// GeoLocalityName returns an attribute KeyValue conforming to the +// "geo.locality.name" semantic conventions. It represents the locality name. +// Represents the name of a city, town, village, or similar populated place. +func GeoLocalityName(val string) attribute.KeyValue { + return GeoLocalityNameKey.String(val) +} + +// GeoLocationLat returns an attribute KeyValue conforming to the +// "geo.location.lat" semantic conventions. It represents the latitude of the geo +// location in [WGS84]. +// +// [WGS84]: https://wikipedia.org/wiki/World_Geodetic_System#WGS84 +func GeoLocationLat(val float64) attribute.KeyValue { + return GeoLocationLatKey.Float64(val) +} + +// GeoLocationLon returns an attribute KeyValue conforming to the +// "geo.location.lon" semantic conventions. It represents the longitude of the +// geo location in [WGS84]. +// +// [WGS84]: https://wikipedia.org/wiki/World_Geodetic_System#WGS84 +func GeoLocationLon(val float64) attribute.KeyValue { + return GeoLocationLonKey.Float64(val) +} + +// GeoPostalCode returns an attribute KeyValue conforming to the +// "geo.postal_code" semantic conventions. It represents the postal code +// associated with the location. Values appropriate for this field may also be +// known as a postcode or ZIP code and will vary widely from country to country. +func GeoPostalCode(val string) attribute.KeyValue { + return GeoPostalCodeKey.String(val) +} + +// GeoRegionISOCode returns an attribute KeyValue conforming to the +// "geo.region.iso_code" semantic conventions. It represents the region ISO code +// ([ISO 3166-2]). +// +// [ISO 3166-2]: https://wikipedia.org/wiki/ISO_3166-2 +func GeoRegionISOCode(val string) attribute.KeyValue { + return GeoRegionISOCodeKey.String(val) +} + +// Enum values for geo.continent.code +var ( + // Africa + // Stability: development + GeoContinentCodeAf = GeoContinentCodeKey.String("AF") + // Antarctica + // Stability: development + GeoContinentCodeAn = GeoContinentCodeKey.String("AN") + // Asia + // Stability: development + GeoContinentCodeAs = GeoContinentCodeKey.String("AS") + // Europe + // Stability: development + GeoContinentCodeEu = GeoContinentCodeKey.String("EU") + // North America + // Stability: development + GeoContinentCodeNa = GeoContinentCodeKey.String("NA") + // Oceania + // Stability: development + GeoContinentCodeOc = GeoContinentCodeKey.String("OC") + // South America + // Stability: development + GeoContinentCodeSa = GeoContinentCodeKey.String("SA") +) + +// Namespace: go +const ( + // GoMemoryTypeKey is the attribute Key conforming to the "go.memory.type" + // semantic conventions. It represents the type of memory. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "other", "stack" + GoMemoryTypeKey = attribute.Key("go.memory.type") +) + +// Enum values for go.memory.type +var ( + // Memory allocated from the heap that is reserved for stack space, whether or + // not it is currently in-use. + // Stability: development + GoMemoryTypeStack = GoMemoryTypeKey.String("stack") + // Memory used by the Go runtime, excluding other categories of memory usage + // described in this enumeration. + // Stability: development + GoMemoryTypeOther = GoMemoryTypeKey.String("other") +) + +// Namespace: graphql +const ( + // GraphQLDocumentKey is the attribute Key conforming to the "graphql.document" + // semantic conventions. It represents the GraphQL document being executed. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: query findBookById { bookById(id: ?) { name } } + // Note: The value may be sanitized to exclude sensitive information. + GraphQLDocumentKey = attribute.Key("graphql.document") + + // GraphQLOperationNameKey is the attribute Key conforming to the + // "graphql.operation.name" semantic conventions. It represents the name of the + // operation being executed. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: findBookById + GraphQLOperationNameKey = attribute.Key("graphql.operation.name") + + // GraphQLOperationTypeKey is the attribute Key conforming to the + // "graphql.operation.type" semantic conventions. It represents the type of the + // operation being executed. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "query", "mutation", "subscription" + GraphQLOperationTypeKey = attribute.Key("graphql.operation.type") +) + +// GraphQLDocument returns an attribute KeyValue conforming to the +// "graphql.document" semantic conventions. It represents the GraphQL document +// being executed. +func GraphQLDocument(val string) attribute.KeyValue { + return GraphQLDocumentKey.String(val) +} + +// GraphQLOperationName returns an attribute KeyValue conforming to the +// "graphql.operation.name" semantic conventions. It represents the name of the +// operation being executed. +func GraphQLOperationName(val string) attribute.KeyValue { + return GraphQLOperationNameKey.String(val) +} + +// Enum values for graphql.operation.type +var ( + // GraphQL query + // Stability: development + GraphQLOperationTypeQuery = GraphQLOperationTypeKey.String("query") + // GraphQL mutation + // Stability: development + GraphQLOperationTypeMutation = GraphQLOperationTypeKey.String("mutation") + // GraphQL subscription + // Stability: development + GraphQLOperationTypeSubscription = GraphQLOperationTypeKey.String("subscription") +) + +// Namespace: heroku +const ( + // HerokuAppIDKey is the attribute Key conforming to the "heroku.app.id" + // semantic conventions. It represents the unique identifier for the + // application. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2daa2797-e42b-4624-9322-ec3f968df4da" + HerokuAppIDKey = attribute.Key("heroku.app.id") + + // HerokuReleaseCommitKey is the attribute Key conforming to the + // "heroku.release.commit" semantic conventions. It represents the commit hash + // for the current release. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "e6134959463efd8966b20e75b913cafe3f5ec" + HerokuReleaseCommitKey = attribute.Key("heroku.release.commit") + + // HerokuReleaseCreationTimestampKey is the attribute Key conforming to the + // "heroku.release.creation_timestamp" semantic conventions. It represents the + // time and date the release was created. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2022-10-23T18:00:42Z" + HerokuReleaseCreationTimestampKey = attribute.Key("heroku.release.creation_timestamp") +) + +// HerokuAppID returns an attribute KeyValue conforming to the "heroku.app.id" +// semantic conventions. It represents the unique identifier for the application. +func HerokuAppID(val string) attribute.KeyValue { + return HerokuAppIDKey.String(val) +} + +// HerokuReleaseCommit returns an attribute KeyValue conforming to the +// "heroku.release.commit" semantic conventions. It represents the commit hash +// for the current release. +func HerokuReleaseCommit(val string) attribute.KeyValue { + return HerokuReleaseCommitKey.String(val) +} + +// HerokuReleaseCreationTimestamp returns an attribute KeyValue conforming to the +// "heroku.release.creation_timestamp" semantic conventions. It represents the +// time and date the release was created. +func HerokuReleaseCreationTimestamp(val string) attribute.KeyValue { + return HerokuReleaseCreationTimestampKey.String(val) +} + +// Namespace: host +const ( + // HostArchKey is the attribute Key conforming to the "host.arch" semantic + // conventions. It represents the CPU architecture the host system is running + // on. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + HostArchKey = attribute.Key("host.arch") + + // HostCPUCacheL2SizeKey is the attribute Key conforming to the + // "host.cpu.cache.l2.size" semantic conventions. It represents the amount of + // level 2 memory cache available to the processor (in Bytes). + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 12288000 + HostCPUCacheL2SizeKey = attribute.Key("host.cpu.cache.l2.size") + + // HostCPUFamilyKey is the attribute Key conforming to the "host.cpu.family" + // semantic conventions. It represents the family or generation of the CPU. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "6", "PA-RISC 1.1e" + HostCPUFamilyKey = attribute.Key("host.cpu.family") + + // HostCPUModelIDKey is the attribute Key conforming to the "host.cpu.model.id" + // semantic conventions. It represents the model identifier. It provides more + // granular information about the CPU, distinguishing it from other CPUs within + // the same family. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "6", "9000/778/B180L" + HostCPUModelIDKey = attribute.Key("host.cpu.model.id") + + // HostCPUModelNameKey is the attribute Key conforming to the + // "host.cpu.model.name" semantic conventions. It represents the model + // designation of the processor. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz" + HostCPUModelNameKey = attribute.Key("host.cpu.model.name") + + // HostCPUSteppingKey is the attribute Key conforming to the "host.cpu.stepping" + // semantic conventions. It represents the stepping or core revisions. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "1", "r1p1" + HostCPUSteppingKey = attribute.Key("host.cpu.stepping") + + // HostCPUVendorIDKey is the attribute Key conforming to the + // "host.cpu.vendor.id" semantic conventions. It represents the processor + // manufacturer identifier. A maximum 12-character string. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "GenuineIntel" + // Note: [CPUID] command returns the vendor ID string in EBX, EDX and ECX + // registers. Writing these to memory in this order results in a 12-character + // string. + // + // [CPUID]: https://wiki.osdev.org/CPUID + HostCPUVendorIDKey = attribute.Key("host.cpu.vendor.id") + + // HostIDKey is the attribute Key conforming to the "host.id" semantic + // conventions. It represents the unique host ID. For Cloud, this must be the + // instance_id assigned by the cloud provider. For non-containerized systems, + // this should be the `machine-id`. See the table below for the sources to use + // to determine the `machine-id` based on operating system. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "fdbf79e8af94cb7f9e8df36789187052" + HostIDKey = attribute.Key("host.id") + + // HostImageIDKey is the attribute Key conforming to the "host.image.id" + // semantic conventions. It represents the VM image ID or host OS image ID. For + // Cloud, this value is from the provider. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "ami-07b06b442921831e5" + HostImageIDKey = attribute.Key("host.image.id") + + // HostImageNameKey is the attribute Key conforming to the "host.image.name" + // semantic conventions. It represents the name of the VM image or OS install + // the host was instantiated from. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "infra-ami-eks-worker-node-7d4ec78312", "CentOS-8-x86_64-1905" + HostImageNameKey = attribute.Key("host.image.name") + + // HostImageVersionKey is the attribute Key conforming to the + // "host.image.version" semantic conventions. It represents the version string + // of the VM image or host OS as defined in [Version Attributes]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "0.1" + // + // [Version Attributes]: /docs/resource/README.md#version-attributes + HostImageVersionKey = attribute.Key("host.image.version") + + // HostIPKey is the attribute Key conforming to the "host.ip" semantic + // conventions. It represents the available IP addresses of the host, excluding + // loopback interfaces. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "192.168.1.140", "fe80::abc2:4a28:737a:609e" + // Note: IPv4 Addresses MUST be specified in dotted-quad notation. IPv6 + // addresses MUST be specified in the [RFC 5952] format. + // + // [RFC 5952]: https://www.rfc-editor.org/rfc/rfc5952.html + HostIPKey = attribute.Key("host.ip") + + // HostMacKey is the attribute Key conforming to the "host.mac" semantic + // conventions. It represents the available MAC addresses of the host, excluding + // loopback interfaces. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "AC-DE-48-23-45-67", "AC-DE-48-23-45-67-01-9F" + // Note: MAC Addresses MUST be represented in [IEEE RA hexadecimal form]: as + // hyphen-separated octets in uppercase hexadecimal form from most to least + // significant. + // + // [IEEE RA hexadecimal form]: https://standards.ieee.org/wp-content/uploads/import/documents/tutorials/eui.pdf + HostMacKey = attribute.Key("host.mac") + + // HostNameKey is the attribute Key conforming to the "host.name" semantic + // conventions. It represents the name of the host. On Unix systems, it may + // contain what the hostname command returns, or the fully qualified hostname, + // or another name specified by the user. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "opentelemetry-test" + HostNameKey = attribute.Key("host.name") + + // HostTypeKey is the attribute Key conforming to the "host.type" semantic + // conventions. It represents the type of host. For Cloud, this must be the + // machine type. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "n1-standard-1" + HostTypeKey = attribute.Key("host.type") +) + +// HostCPUCacheL2Size returns an attribute KeyValue conforming to the +// "host.cpu.cache.l2.size" semantic conventions. It represents the amount of +// level 2 memory cache available to the processor (in Bytes). +func HostCPUCacheL2Size(val int) attribute.KeyValue { + return HostCPUCacheL2SizeKey.Int(val) +} + +// HostCPUFamily returns an attribute KeyValue conforming to the +// "host.cpu.family" semantic conventions. It represents the family or generation +// of the CPU. +func HostCPUFamily(val string) attribute.KeyValue { + return HostCPUFamilyKey.String(val) +} + +// HostCPUModelID returns an attribute KeyValue conforming to the +// "host.cpu.model.id" semantic conventions. It represents the model identifier. +// It provides more granular information about the CPU, distinguishing it from +// other CPUs within the same family. +func HostCPUModelID(val string) attribute.KeyValue { + return HostCPUModelIDKey.String(val) +} + +// HostCPUModelName returns an attribute KeyValue conforming to the +// "host.cpu.model.name" semantic conventions. It represents the model +// designation of the processor. +func HostCPUModelName(val string) attribute.KeyValue { + return HostCPUModelNameKey.String(val) +} + +// HostCPUStepping returns an attribute KeyValue conforming to the +// "host.cpu.stepping" semantic conventions. It represents the stepping or core +// revisions. +func HostCPUStepping(val string) attribute.KeyValue { + return HostCPUSteppingKey.String(val) +} + +// HostCPUVendorID returns an attribute KeyValue conforming to the +// "host.cpu.vendor.id" semantic conventions. It represents the processor +// manufacturer identifier. A maximum 12-character string. +func HostCPUVendorID(val string) attribute.KeyValue { + return HostCPUVendorIDKey.String(val) +} + +// HostID returns an attribute KeyValue conforming to the "host.id" semantic +// conventions. It represents the unique host ID. For Cloud, this must be the +// instance_id assigned by the cloud provider. For non-containerized systems, +// this should be the `machine-id`. See the table below for the sources to use to +// determine the `machine-id` based on operating system. +func HostID(val string) attribute.KeyValue { + return HostIDKey.String(val) +} + +// HostImageID returns an attribute KeyValue conforming to the "host.image.id" +// semantic conventions. It represents the VM image ID or host OS image ID. For +// Cloud, this value is from the provider. +func HostImageID(val string) attribute.KeyValue { + return HostImageIDKey.String(val) +} + +// HostImageName returns an attribute KeyValue conforming to the +// "host.image.name" semantic conventions. It represents the name of the VM image +// or OS install the host was instantiated from. +func HostImageName(val string) attribute.KeyValue { + return HostImageNameKey.String(val) +} + +// HostImageVersion returns an attribute KeyValue conforming to the +// "host.image.version" semantic conventions. It represents the version string of +// the VM image or host OS as defined in [Version Attributes]. +// +// [Version Attributes]: /docs/resource/README.md#version-attributes +func HostImageVersion(val string) attribute.KeyValue { + return HostImageVersionKey.String(val) +} + +// HostIP returns an attribute KeyValue conforming to the "host.ip" semantic +// conventions. It represents the available IP addresses of the host, excluding +// loopback interfaces. +func HostIP(val ...string) attribute.KeyValue { + return HostIPKey.StringSlice(val) +} + +// HostMac returns an attribute KeyValue conforming to the "host.mac" semantic +// conventions. It represents the available MAC addresses of the host, excluding +// loopback interfaces. +func HostMac(val ...string) attribute.KeyValue { + return HostMacKey.StringSlice(val) +} + +// HostName returns an attribute KeyValue conforming to the "host.name" semantic +// conventions. It represents the name of the host. On Unix systems, it may +// contain what the hostname command returns, or the fully qualified hostname, or +// another name specified by the user. +func HostName(val string) attribute.KeyValue { + return HostNameKey.String(val) +} + +// HostType returns an attribute KeyValue conforming to the "host.type" semantic +// conventions. It represents the type of host. For Cloud, this must be the +// machine type. +func HostType(val string) attribute.KeyValue { + return HostTypeKey.String(val) +} + +// Enum values for host.arch +var ( + // AMD64 + // Stability: development + HostArchAMD64 = HostArchKey.String("amd64") + // ARM32 + // Stability: development + HostArchARM32 = HostArchKey.String("arm32") + // ARM64 + // Stability: development + HostArchARM64 = HostArchKey.String("arm64") + // Itanium + // Stability: development + HostArchIA64 = HostArchKey.String("ia64") + // 32-bit PowerPC + // Stability: development + HostArchPPC32 = HostArchKey.String("ppc32") + // 64-bit PowerPC + // Stability: development + HostArchPPC64 = HostArchKey.String("ppc64") + // IBM z/Architecture + // Stability: development + HostArchS390x = HostArchKey.String("s390x") + // 32-bit x86 + // Stability: development + HostArchX86 = HostArchKey.String("x86") +) + +// Namespace: http +const ( + // HTTPConnectionStateKey is the attribute Key conforming to the + // "http.connection.state" semantic conventions. It represents the state of the + // HTTP connection in the HTTP connection pool. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "active", "idle" + HTTPConnectionStateKey = attribute.Key("http.connection.state") + + // HTTPRequestBodySizeKey is the attribute Key conforming to the + // "http.request.body.size" semantic conventions. It represents the size of the + // request payload body in bytes. This is the number of bytes transferred + // excluding headers and is often, but not always, present as the + // [Content-Length] header. For requests using transport encoding, this should + // be the compressed size. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // [Content-Length]: https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length + HTTPRequestBodySizeKey = attribute.Key("http.request.body.size") + + // HTTPRequestMethodKey is the attribute Key conforming to the + // "http.request.method" semantic conventions. It represents the HTTP request + // method. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "GET", "POST", "HEAD" + // Note: HTTP request method value SHOULD be "known" to the instrumentation. + // By default, this convention defines "known" methods as the ones listed in + // [RFC9110], + // the PATCH method defined in [RFC5789] + // and the QUERY method defined in [httpbis-safe-method-w-body]. + // + // If the HTTP request method is not known to instrumentation, it MUST set the + // `http.request.method` attribute to `_OTHER`. + // + // If the HTTP instrumentation could end up converting valid HTTP request + // methods to `_OTHER`, then it MUST provide a way to override + // the list of known HTTP methods. If this override is done via environment + // variable, then the environment variable MUST be named + // OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated list of + // case-sensitive known HTTP methods + // (this list MUST be a full override of the default known method, it is not a + // list of known methods in addition to the defaults). + // + // HTTP method names are case-sensitive and `http.request.method` attribute + // value MUST match a known HTTP method name exactly. + // Instrumentations for specific web frameworks that consider HTTP methods to be + // case insensitive, SHOULD populate a canonical equivalent. + // Tracing instrumentations that do so, MUST also set + // `http.request.method_original` to the original value. + // + // [RFC9110]: https://www.rfc-editor.org/rfc/rfc9110.html#name-methods + // [RFC5789]: https://www.rfc-editor.org/rfc/rfc5789.html + // [httpbis-safe-method-w-body]: https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/?include_text=1 + HTTPRequestMethodKey = attribute.Key("http.request.method") + + // HTTPRequestMethodOriginalKey is the attribute Key conforming to the + // "http.request.method_original" semantic conventions. It represents the + // original HTTP method sent by the client in the request line. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "GeT", "ACL", "foo" + HTTPRequestMethodOriginalKey = attribute.Key("http.request.method_original") + + // HTTPRequestResendCountKey is the attribute Key conforming to the + // "http.request.resend_count" semantic conventions. It represents the ordinal + // number of request resending attempt (for any reason, including redirects). + // + // Type: int + // RequirementLevel: Recommended + // Stability: Stable + // + // Note: The resend count SHOULD be updated each time an HTTP request gets + // resent by the client, regardless of what was the cause of the resending (e.g. + // redirection, authorization failure, 503 Server Unavailable, network issues, + // or any other). + HTTPRequestResendCountKey = attribute.Key("http.request.resend_count") + + // HTTPRequestSizeKey is the attribute Key conforming to the "http.request.size" + // semantic conventions. It represents the total size of the request in bytes. + // This should be the total number of bytes sent over the wire, including the + // request line (HTTP/1.1), framing (HTTP/2 and HTTP/3), headers, and request + // body if any. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + HTTPRequestSizeKey = attribute.Key("http.request.size") + + // HTTPResponseBodySizeKey is the attribute Key conforming to the + // "http.response.body.size" semantic conventions. It represents the size of the + // response payload body in bytes. This is the number of bytes transferred + // excluding headers and is often, but not always, present as the + // [Content-Length] header. For requests using transport encoding, this should + // be the compressed size. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // [Content-Length]: https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length + HTTPResponseBodySizeKey = attribute.Key("http.response.body.size") + + // HTTPResponseSizeKey is the attribute Key conforming to the + // "http.response.size" semantic conventions. It represents the total size of + // the response in bytes. This should be the total number of bytes sent over the + // wire, including the status line (HTTP/1.1), framing (HTTP/2 and HTTP/3), + // headers, and response body and trailers if any. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + HTTPResponseSizeKey = attribute.Key("http.response.size") + + // HTTPResponseStatusCodeKey is the attribute Key conforming to the + // "http.response.status_code" semantic conventions. It represents the + // [HTTP response status code]. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: 200 + // + // [HTTP response status code]: https://tools.ietf.org/html/rfc7231#section-6 + HTTPResponseStatusCodeKey = attribute.Key("http.response.status_code") + + // HTTPRouteKey is the attribute Key conforming to the "http.route" semantic + // conventions. It represents the matched route template for the request. This + // MUST be low-cardinality and include all static path segments, with dynamic + // path segments represented with placeholders. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "/users/:userID?", "my-controller/my-action/{id?}" + // Note: MUST NOT be populated when this is not supported by the HTTP server + // framework as the route attribute should have low-cardinality and the URI path + // can NOT substitute it. + // SHOULD include the [application root] if there is one. + // + // A static path segment is a part of the route template with a fixed, + // low-cardinality value. This includes literal strings like `/users/` and + // placeholders that + // are constrained to a finite, predefined set of values, e.g. `{controller}` or + // `{action}`. + // + // A dynamic path segment is a placeholder for a value that can have high + // cardinality and is not constrained to a predefined list like static path + // segments. + // + // Instrumentations SHOULD use routing information provided by the corresponding + // web framework. They SHOULD pick the most precise source of routing + // information and MAY + // support custom route formatting. Instrumentations SHOULD document the format + // and the API used to obtain the route string. + // + // [application root]: /docs/http/http-spans.md#http-server-definitions + HTTPRouteKey = attribute.Key("http.route") +) + +// HTTPRequestBodySize returns an attribute KeyValue conforming to the +// "http.request.body.size" semantic conventions. It represents the size of the +// request payload body in bytes. This is the number of bytes transferred +// excluding headers and is often, but not always, present as the +// [Content-Length] header. For requests using transport encoding, this should be +// the compressed size. +// +// [Content-Length]: https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length +func HTTPRequestBodySize(val int) attribute.KeyValue { + return HTTPRequestBodySizeKey.Int(val) +} + +// HTTPRequestHeader returns an attribute KeyValue conforming to the +// "http.request.header" semantic conventions. It represents the HTTP request +// headers, `` being the normalized HTTP Header name (lowercase), the value +// being the header values. +func HTTPRequestHeader(key string, val ...string) attribute.KeyValue { + return attribute.StringSlice("http.request.header."+key, val) +} + +// HTTPRequestMethodOriginal returns an attribute KeyValue conforming to the +// "http.request.method_original" semantic conventions. It represents the +// original HTTP method sent by the client in the request line. +func HTTPRequestMethodOriginal(val string) attribute.KeyValue { + return HTTPRequestMethodOriginalKey.String(val) +} + +// HTTPRequestResendCount returns an attribute KeyValue conforming to the +// "http.request.resend_count" semantic conventions. It represents the ordinal +// number of request resending attempt (for any reason, including redirects). +func HTTPRequestResendCount(val int) attribute.KeyValue { + return HTTPRequestResendCountKey.Int(val) +} + +// HTTPRequestSize returns an attribute KeyValue conforming to the +// "http.request.size" semantic conventions. It represents the total size of the +// request in bytes. This should be the total number of bytes sent over the wire, +// including the request line (HTTP/1.1), framing (HTTP/2 and HTTP/3), headers, +// and request body if any. +func HTTPRequestSize(val int) attribute.KeyValue { + return HTTPRequestSizeKey.Int(val) +} + +// HTTPResponseBodySize returns an attribute KeyValue conforming to the +// "http.response.body.size" semantic conventions. It represents the size of the +// response payload body in bytes. This is the number of bytes transferred +// excluding headers and is often, but not always, present as the +// [Content-Length] header. For requests using transport encoding, this should be +// the compressed size. +// +// [Content-Length]: https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length +func HTTPResponseBodySize(val int) attribute.KeyValue { + return HTTPResponseBodySizeKey.Int(val) +} + +// HTTPResponseHeader returns an attribute KeyValue conforming to the +// "http.response.header" semantic conventions. It represents the HTTP response +// headers, `` being the normalized HTTP Header name (lowercase), the value +// being the header values. +func HTTPResponseHeader(key string, val ...string) attribute.KeyValue { + return attribute.StringSlice("http.response.header."+key, val) +} + +// HTTPResponseSize returns an attribute KeyValue conforming to the +// "http.response.size" semantic conventions. It represents the total size of the +// response in bytes. This should be the total number of bytes sent over the +// wire, including the status line (HTTP/1.1), framing (HTTP/2 and HTTP/3), +// headers, and response body and trailers if any. +func HTTPResponseSize(val int) attribute.KeyValue { + return HTTPResponseSizeKey.Int(val) +} + +// HTTPResponseStatusCode returns an attribute KeyValue conforming to the +// "http.response.status_code" semantic conventions. It represents the +// [HTTP response status code]. +// +// [HTTP response status code]: https://tools.ietf.org/html/rfc7231#section-6 +func HTTPResponseStatusCode(val int) attribute.KeyValue { + return HTTPResponseStatusCodeKey.Int(val) +} + +// HTTPRoute returns an attribute KeyValue conforming to the "http.route" +// semantic conventions. It represents the matched route template for the +// request. This MUST be low-cardinality and include all static path segments, +// with dynamic path segments represented with placeholders. +func HTTPRoute(val string) attribute.KeyValue { + return HTTPRouteKey.String(val) +} + +// Enum values for http.connection.state +var ( + // active state. + // Stability: development + HTTPConnectionStateActive = HTTPConnectionStateKey.String("active") + // idle state. + // Stability: development + HTTPConnectionStateIdle = HTTPConnectionStateKey.String("idle") +) + +// Enum values for http.request.method +var ( + // CONNECT method. + // Stability: stable + HTTPRequestMethodConnect = HTTPRequestMethodKey.String("CONNECT") + // DELETE method. + // Stability: stable + HTTPRequestMethodDelete = HTTPRequestMethodKey.String("DELETE") + // GET method. + // Stability: stable + HTTPRequestMethodGet = HTTPRequestMethodKey.String("GET") + // HEAD method. + // Stability: stable + HTTPRequestMethodHead = HTTPRequestMethodKey.String("HEAD") + // OPTIONS method. + // Stability: stable + HTTPRequestMethodOptions = HTTPRequestMethodKey.String("OPTIONS") + // PATCH method. + // Stability: stable + HTTPRequestMethodPatch = HTTPRequestMethodKey.String("PATCH") + // POST method. + // Stability: stable + HTTPRequestMethodPost = HTTPRequestMethodKey.String("POST") + // PUT method. + // Stability: stable + HTTPRequestMethodPut = HTTPRequestMethodKey.String("PUT") + // TRACE method. + // Stability: stable + HTTPRequestMethodTrace = HTTPRequestMethodKey.String("TRACE") + // QUERY method. + // Stability: development + HTTPRequestMethodQuery = HTTPRequestMethodKey.String("QUERY") + // Any HTTP method that the instrumentation has no prior knowledge of. + // Stability: stable + HTTPRequestMethodOther = HTTPRequestMethodKey.String("_OTHER") +) + +// Namespace: hw +const ( + // HwBatteryCapacityKey is the attribute Key conforming to the + // "hw.battery.capacity" semantic conventions. It represents the design capacity + // in Watts-hours or Amper-hours. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "9.3Ah", "50Wh" + HwBatteryCapacityKey = attribute.Key("hw.battery.capacity") + + // HwBatteryChemistryKey is the attribute Key conforming to the + // "hw.battery.chemistry" semantic conventions. It represents the battery + // [chemistry], e.g. Lithium-Ion, Nickel-Cadmium, etc. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Li-ion", "NiMH" + // + // [chemistry]: https://schemas.dmtf.org/wbem/cim-html/2.31.0/CIM_Battery.html + HwBatteryChemistryKey = attribute.Key("hw.battery.chemistry") + + // HwBatteryStateKey is the attribute Key conforming to the "hw.battery.state" + // semantic conventions. It represents the current state of the battery. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + HwBatteryStateKey = attribute.Key("hw.battery.state") + + // HwBiosVersionKey is the attribute Key conforming to the "hw.bios_version" + // semantic conventions. It represents the BIOS version of the hardware + // component. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "1.2.3" + HwBiosVersionKey = attribute.Key("hw.bios_version") + + // HwDriverVersionKey is the attribute Key conforming to the "hw.driver_version" + // semantic conventions. It represents the driver version for the hardware + // component. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "10.2.1-3" + HwDriverVersionKey = attribute.Key("hw.driver_version") + + // HwEnclosureTypeKey is the attribute Key conforming to the "hw.enclosure.type" + // semantic conventions. It represents the type of the enclosure (useful for + // modular systems). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Computer", "Storage", "Switch" + HwEnclosureTypeKey = attribute.Key("hw.enclosure.type") + + // HwFirmwareVersionKey is the attribute Key conforming to the + // "hw.firmware_version" semantic conventions. It represents the firmware + // version of the hardware component. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2.0.1" + HwFirmwareVersionKey = attribute.Key("hw.firmware_version") + + // HwGpuTaskKey is the attribute Key conforming to the "hw.gpu.task" semantic + // conventions. It represents the type of task the GPU is performing. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + HwGpuTaskKey = attribute.Key("hw.gpu.task") + + // HwIDKey is the attribute Key conforming to the "hw.id" semantic conventions. + // It represents an identifier for the hardware component, unique within the + // monitored host. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "win32battery_battery_testsysa33_1" + HwIDKey = attribute.Key("hw.id") + + // HwLimitTypeKey is the attribute Key conforming to the "hw.limit_type" + // semantic conventions. It represents the type of limit for hardware + // components. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + HwLimitTypeKey = attribute.Key("hw.limit_type") + + // HwLogicalDiskRaidLevelKey is the attribute Key conforming to the + // "hw.logical_disk.raid_level" semantic conventions. It represents the RAID + // Level of the logical disk. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "RAID0+1", "RAID5", "RAID10" + HwLogicalDiskRaidLevelKey = attribute.Key("hw.logical_disk.raid_level") + + // HwLogicalDiskStateKey is the attribute Key conforming to the + // "hw.logical_disk.state" semantic conventions. It represents the state of the + // logical disk space usage. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + HwLogicalDiskStateKey = attribute.Key("hw.logical_disk.state") + + // HwMemoryTypeKey is the attribute Key conforming to the "hw.memory.type" + // semantic conventions. It represents the type of the memory module. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "DDR4", "DDR5", "LPDDR5" + HwMemoryTypeKey = attribute.Key("hw.memory.type") + + // HwModelKey is the attribute Key conforming to the "hw.model" semantic + // conventions. It represents the descriptive model name of the hardware + // component. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "PERC H740P", "Intel(R) Core(TM) i7-10700K", "Dell XPS 15 Battery" + HwModelKey = attribute.Key("hw.model") + + // HwNameKey is the attribute Key conforming to the "hw.name" semantic + // conventions. It represents an easily-recognizable name for the hardware + // component. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "eth0" + HwNameKey = attribute.Key("hw.name") + + // HwNetworkLogicalAddressesKey is the attribute Key conforming to the + // "hw.network.logical_addresses" semantic conventions. It represents the + // logical addresses of the adapter (e.g. IP address, or WWPN). + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "172.16.8.21", "57.11.193.42" + HwNetworkLogicalAddressesKey = attribute.Key("hw.network.logical_addresses") + + // HwNetworkPhysicalAddressKey is the attribute Key conforming to the + // "hw.network.physical_address" semantic conventions. It represents the + // physical address of the adapter (e.g. MAC address, or WWNN). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "00-90-F5-E9-7B-36" + HwNetworkPhysicalAddressKey = attribute.Key("hw.network.physical_address") + + // HwParentKey is the attribute Key conforming to the "hw.parent" semantic + // conventions. It represents the unique identifier of the parent component + // (typically the `hw.id` attribute of the enclosure, or disk controller). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "dellStorage_perc_0" + HwParentKey = attribute.Key("hw.parent") + + // HwPhysicalDiskSmartAttributeKey is the attribute Key conforming to the + // "hw.physical_disk.smart_attribute" semantic conventions. It represents the + // [S.M.A.R.T.] (Self-Monitoring, Analysis, and Reporting Technology) attribute + // of the physical disk. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Spin Retry Count", "Seek Error Rate", "Raw Read Error Rate" + // + // [S.M.A.R.T.]: https://wikipedia.org/wiki/S.M.A.R.T. + HwPhysicalDiskSmartAttributeKey = attribute.Key("hw.physical_disk.smart_attribute") + + // HwPhysicalDiskStateKey is the attribute Key conforming to the + // "hw.physical_disk.state" semantic conventions. It represents the state of the + // physical disk endurance utilization. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + HwPhysicalDiskStateKey = attribute.Key("hw.physical_disk.state") + + // HwPhysicalDiskTypeKey is the attribute Key conforming to the + // "hw.physical_disk.type" semantic conventions. It represents the type of the + // physical disk. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "HDD", "SSD", "10K" + HwPhysicalDiskTypeKey = attribute.Key("hw.physical_disk.type") + + // HwSensorLocationKey is the attribute Key conforming to the + // "hw.sensor_location" semantic conventions. It represents the location of the + // sensor. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "cpu0", "ps1", "INLET", "CPU0_DIE", "AMBIENT", "MOTHERBOARD", "PS0 + // V3_3", "MAIN_12V", "CPU_VCORE" + HwSensorLocationKey = attribute.Key("hw.sensor_location") + + // HwSerialNumberKey is the attribute Key conforming to the "hw.serial_number" + // semantic conventions. It represents the serial number of the hardware + // component. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "CNFCP0123456789" + HwSerialNumberKey = attribute.Key("hw.serial_number") + + // HwStateKey is the attribute Key conforming to the "hw.state" semantic + // conventions. It represents the current state of the component. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + HwStateKey = attribute.Key("hw.state") + + // HwTapeDriveOperationTypeKey is the attribute Key conforming to the + // "hw.tape_drive.operation_type" semantic conventions. It represents the type + // of tape drive operation. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + HwTapeDriveOperationTypeKey = attribute.Key("hw.tape_drive.operation_type") + + // HwTypeKey is the attribute Key conforming to the "hw.type" semantic + // conventions. It represents the type of the component. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: Describes the category of the hardware component for which `hw.state` + // is being reported. For example, `hw.type=temperature` along with + // `hw.state=degraded` would indicate that the temperature of the hardware + // component has been reported as `degraded`. + HwTypeKey = attribute.Key("hw.type") + + // HwVendorKey is the attribute Key conforming to the "hw.vendor" semantic + // conventions. It represents the vendor name of the hardware component. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Dell", "HP", "Intel", "AMD", "LSI", "Lenovo" + HwVendorKey = attribute.Key("hw.vendor") +) + +// HwBatteryCapacity returns an attribute KeyValue conforming to the +// "hw.battery.capacity" semantic conventions. It represents the design capacity +// in Watts-hours or Amper-hours. +func HwBatteryCapacity(val string) attribute.KeyValue { + return HwBatteryCapacityKey.String(val) +} + +// HwBatteryChemistry returns an attribute KeyValue conforming to the +// "hw.battery.chemistry" semantic conventions. It represents the battery +// [chemistry], e.g. Lithium-Ion, Nickel-Cadmium, etc. +// +// [chemistry]: https://schemas.dmtf.org/wbem/cim-html/2.31.0/CIM_Battery.html +func HwBatteryChemistry(val string) attribute.KeyValue { + return HwBatteryChemistryKey.String(val) +} + +// HwBiosVersion returns an attribute KeyValue conforming to the +// "hw.bios_version" semantic conventions. It represents the BIOS version of the +// hardware component. +func HwBiosVersion(val string) attribute.KeyValue { + return HwBiosVersionKey.String(val) +} + +// HwDriverVersion returns an attribute KeyValue conforming to the +// "hw.driver_version" semantic conventions. It represents the driver version for +// the hardware component. +func HwDriverVersion(val string) attribute.KeyValue { + return HwDriverVersionKey.String(val) +} + +// HwEnclosureType returns an attribute KeyValue conforming to the +// "hw.enclosure.type" semantic conventions. It represents the type of the +// enclosure (useful for modular systems). +func HwEnclosureType(val string) attribute.KeyValue { + return HwEnclosureTypeKey.String(val) +} + +// HwFirmwareVersion returns an attribute KeyValue conforming to the +// "hw.firmware_version" semantic conventions. It represents the firmware version +// of the hardware component. +func HwFirmwareVersion(val string) attribute.KeyValue { + return HwFirmwareVersionKey.String(val) +} + +// HwID returns an attribute KeyValue conforming to the "hw.id" semantic +// conventions. It represents an identifier for the hardware component, unique +// within the monitored host. +func HwID(val string) attribute.KeyValue { + return HwIDKey.String(val) +} + +// HwLogicalDiskRaidLevel returns an attribute KeyValue conforming to the +// "hw.logical_disk.raid_level" semantic conventions. It represents the RAID +// Level of the logical disk. +func HwLogicalDiskRaidLevel(val string) attribute.KeyValue { + return HwLogicalDiskRaidLevelKey.String(val) +} + +// HwMemoryType returns an attribute KeyValue conforming to the "hw.memory.type" +// semantic conventions. It represents the type of the memory module. +func HwMemoryType(val string) attribute.KeyValue { + return HwMemoryTypeKey.String(val) +} + +// HwModel returns an attribute KeyValue conforming to the "hw.model" semantic +// conventions. It represents the descriptive model name of the hardware +// component. +func HwModel(val string) attribute.KeyValue { + return HwModelKey.String(val) +} + +// HwName returns an attribute KeyValue conforming to the "hw.name" semantic +// conventions. It represents an easily-recognizable name for the hardware +// component. +func HwName(val string) attribute.KeyValue { + return HwNameKey.String(val) +} + +// HwNetworkLogicalAddresses returns an attribute KeyValue conforming to the +// "hw.network.logical_addresses" semantic conventions. It represents the logical +// addresses of the adapter (e.g. IP address, or WWPN). +func HwNetworkLogicalAddresses(val ...string) attribute.KeyValue { + return HwNetworkLogicalAddressesKey.StringSlice(val) +} + +// HwNetworkPhysicalAddress returns an attribute KeyValue conforming to the +// "hw.network.physical_address" semantic conventions. It represents the physical +// address of the adapter (e.g. MAC address, or WWNN). +func HwNetworkPhysicalAddress(val string) attribute.KeyValue { + return HwNetworkPhysicalAddressKey.String(val) +} + +// HwParent returns an attribute KeyValue conforming to the "hw.parent" semantic +// conventions. It represents the unique identifier of the parent component +// (typically the `hw.id` attribute of the enclosure, or disk controller). +func HwParent(val string) attribute.KeyValue { + return HwParentKey.String(val) +} + +// HwPhysicalDiskSmartAttribute returns an attribute KeyValue conforming to the +// "hw.physical_disk.smart_attribute" semantic conventions. It represents the +// [S.M.A.R.T.] (Self-Monitoring, Analysis, and Reporting Technology) attribute +// of the physical disk. +// +// [S.M.A.R.T.]: https://wikipedia.org/wiki/S.M.A.R.T. +func HwPhysicalDiskSmartAttribute(val string) attribute.KeyValue { + return HwPhysicalDiskSmartAttributeKey.String(val) +} + +// HwPhysicalDiskType returns an attribute KeyValue conforming to the +// "hw.physical_disk.type" semantic conventions. It represents the type of the +// physical disk. +func HwPhysicalDiskType(val string) attribute.KeyValue { + return HwPhysicalDiskTypeKey.String(val) +} + +// HwSensorLocation returns an attribute KeyValue conforming to the +// "hw.sensor_location" semantic conventions. It represents the location of the +// sensor. +func HwSensorLocation(val string) attribute.KeyValue { + return HwSensorLocationKey.String(val) +} + +// HwSerialNumber returns an attribute KeyValue conforming to the +// "hw.serial_number" semantic conventions. It represents the serial number of +// the hardware component. +func HwSerialNumber(val string) attribute.KeyValue { + return HwSerialNumberKey.String(val) +} + +// HwVendor returns an attribute KeyValue conforming to the "hw.vendor" semantic +// conventions. It represents the vendor name of the hardware component. +func HwVendor(val string) attribute.KeyValue { + return HwVendorKey.String(val) +} + +// Enum values for hw.battery.state +var ( + // Charging + // Stability: development + HwBatteryStateCharging = HwBatteryStateKey.String("charging") + // Discharging + // Stability: development + HwBatteryStateDischarging = HwBatteryStateKey.String("discharging") +) + +// Enum values for hw.gpu.task +var ( + // Decoder + // Stability: development + HwGpuTaskDecoder = HwGpuTaskKey.String("decoder") + // Encoder + // Stability: development + HwGpuTaskEncoder = HwGpuTaskKey.String("encoder") + // General + // Stability: development + HwGpuTaskGeneral = HwGpuTaskKey.String("general") +) + +// Enum values for hw.limit_type +var ( + // Critical + // Stability: development + HwLimitTypeCritical = HwLimitTypeKey.String("critical") + // Degraded + // Stability: development + HwLimitTypeDegraded = HwLimitTypeKey.String("degraded") + // High Critical + // Stability: development + HwLimitTypeHighCritical = HwLimitTypeKey.String("high.critical") + // High Degraded + // Stability: development + HwLimitTypeHighDegraded = HwLimitTypeKey.String("high.degraded") + // Low Critical + // Stability: development + HwLimitTypeLowCritical = HwLimitTypeKey.String("low.critical") + // Low Degraded + // Stability: development + HwLimitTypeLowDegraded = HwLimitTypeKey.String("low.degraded") + // Maximum + // Stability: development + HwLimitTypeMax = HwLimitTypeKey.String("max") + // Throttled + // Stability: development + HwLimitTypeThrottled = HwLimitTypeKey.String("throttled") + // Turbo + // Stability: development + HwLimitTypeTurbo = HwLimitTypeKey.String("turbo") +) + +// Enum values for hw.logical_disk.state +var ( + // Used + // Stability: development + HwLogicalDiskStateUsed = HwLogicalDiskStateKey.String("used") + // Free + // Stability: development + HwLogicalDiskStateFree = HwLogicalDiskStateKey.String("free") +) + +// Enum values for hw.physical_disk.state +var ( + // Remaining + // Stability: development + HwPhysicalDiskStateRemaining = HwPhysicalDiskStateKey.String("remaining") +) + +// Enum values for hw.state +var ( + // Degraded + // Stability: development + HwStateDegraded = HwStateKey.String("degraded") + // Failed + // Stability: development + HwStateFailed = HwStateKey.String("failed") + // Needs Cleaning + // Stability: development + HwStateNeedsCleaning = HwStateKey.String("needs_cleaning") + // OK + // Stability: development + HwStateOk = HwStateKey.String("ok") + // Predicted Failure + // Stability: development + HwStatePredictedFailure = HwStateKey.String("predicted_failure") +) + +// Enum values for hw.tape_drive.operation_type +var ( + // Mount + // Stability: development + HwTapeDriveOperationTypeMount = HwTapeDriveOperationTypeKey.String("mount") + // Unmount + // Stability: development + HwTapeDriveOperationTypeUnmount = HwTapeDriveOperationTypeKey.String("unmount") + // Clean + // Stability: development + HwTapeDriveOperationTypeClean = HwTapeDriveOperationTypeKey.String("clean") +) + +// Enum values for hw.type +var ( + // Battery + // Stability: development + HwTypeBattery = HwTypeKey.String("battery") + // CPU + // Stability: development + HwTypeCPU = HwTypeKey.String("cpu") + // Disk controller + // Stability: development + HwTypeDiskController = HwTypeKey.String("disk_controller") + // Enclosure + // Stability: development + HwTypeEnclosure = HwTypeKey.String("enclosure") + // Fan + // Stability: development + HwTypeFan = HwTypeKey.String("fan") + // GPU + // Stability: development + HwTypeGpu = HwTypeKey.String("gpu") + // Logical disk + // Stability: development + HwTypeLogicalDisk = HwTypeKey.String("logical_disk") + // Memory + // Stability: development + HwTypeMemory = HwTypeKey.String("memory") + // Network + // Stability: development + HwTypeNetwork = HwTypeKey.String("network") + // Physical disk + // Stability: development + HwTypePhysicalDisk = HwTypeKey.String("physical_disk") + // Power supply + // Stability: development + HwTypePowerSupply = HwTypeKey.String("power_supply") + // Tape drive + // Stability: development + HwTypeTapeDrive = HwTypeKey.String("tape_drive") + // Temperature + // Stability: development + HwTypeTemperature = HwTypeKey.String("temperature") + // Voltage + // Stability: development + HwTypeVoltage = HwTypeKey.String("voltage") +) + +// Namespace: ios +const ( + // IOSAppStateKey is the attribute Key conforming to the "ios.app.state" + // semantic conventions. It represents the this attribute represents the state + // of the application. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: The iOS lifecycle states are defined in the + // [UIApplicationDelegate documentation], and from which the `OS terminology` + // column values are derived. + // + // [UIApplicationDelegate documentation]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate + IOSAppStateKey = attribute.Key("ios.app.state") +) + +// Enum values for ios.app.state +var ( + // The app has become `active`. Associated with UIKit notification + // `applicationDidBecomeActive`. + // + // Stability: development + IOSAppStateActive = IOSAppStateKey.String("active") + // The app is now `inactive`. Associated with UIKit notification + // `applicationWillResignActive`. + // + // Stability: development + IOSAppStateInactive = IOSAppStateKey.String("inactive") + // The app is now in the background. This value is associated with UIKit + // notification `applicationDidEnterBackground`. + // + // Stability: development + IOSAppStateBackground = IOSAppStateKey.String("background") + // The app is now in the foreground. This value is associated with UIKit + // notification `applicationWillEnterForeground`. + // + // Stability: development + IOSAppStateForeground = IOSAppStateKey.String("foreground") + // The app is about to terminate. Associated with UIKit notification + // `applicationWillTerminate`. + // + // Stability: development + IOSAppStateTerminate = IOSAppStateKey.String("terminate") +) + +// Namespace: jsonrpc +const ( + // JSONRPCProtocolVersionKey is the attribute Key conforming to the + // "jsonrpc.protocol.version" semantic conventions. It represents the protocol + // version, as specified in the `jsonrpc` property of the request and its + // corresponding response. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2.0", "1.0" + JSONRPCProtocolVersionKey = attribute.Key("jsonrpc.protocol.version") + + // JSONRPCRequestIDKey is the attribute Key conforming to the + // "jsonrpc.request.id" semantic conventions. It represents a string + // representation of the `id` property of the request and its corresponding + // response. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "10", "request-7" + // Note: Under the [JSON-RPC specification], the `id` property may be a string, + // number, null, or omitted entirely. When omitted, the request is treated as a + // notification. Using `null` is not equivalent to omitting the `id`, but it is + // discouraged. + // Instrumentations SHOULD NOT capture this attribute when the `id` is `null` or + // omitted. + // + // [JSON-RPC specification]: https://www.jsonrpc.org/specification + JSONRPCRequestIDKey = attribute.Key("jsonrpc.request.id") +) + +// JSONRPCProtocolVersion returns an attribute KeyValue conforming to the +// "jsonrpc.protocol.version" semantic conventions. It represents the protocol +// version, as specified in the `jsonrpc` property of the request and its +// corresponding response. +func JSONRPCProtocolVersion(val string) attribute.KeyValue { + return JSONRPCProtocolVersionKey.String(val) +} + +// JSONRPCRequestID returns an attribute KeyValue conforming to the +// "jsonrpc.request.id" semantic conventions. It represents a string +// representation of the `id` property of the request and its corresponding +// response. +func JSONRPCRequestID(val string) attribute.KeyValue { + return JSONRPCRequestIDKey.String(val) +} + +// Namespace: k8s +const ( + // K8SClusterNameKey is the attribute Key conforming to the "k8s.cluster.name" + // semantic conventions. It represents the name of the cluster. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "opentelemetry-cluster" + K8SClusterNameKey = attribute.Key("k8s.cluster.name") + + // K8SClusterUIDKey is the attribute Key conforming to the "k8s.cluster.uid" + // semantic conventions. It represents a pseudo-ID for the cluster, set to the + // UID of the `kube-system` namespace. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "218fc5a9-a5f1-4b54-aa05-46717d0ab26d" + // Note: K8s doesn't have support for obtaining a cluster ID. If this is ever + // added, we will recommend collecting the `k8s.cluster.uid` through the + // official APIs. In the meantime, we are able to use the `uid` of the + // `kube-system` namespace as a proxy for cluster ID. Read on for the + // rationale. + // + // Every object created in a K8s cluster is assigned a distinct UID. The + // `kube-system` namespace is used by Kubernetes itself and will exist + // for the lifetime of the cluster. Using the `uid` of the `kube-system` + // namespace is a reasonable proxy for the K8s ClusterID as it will only + // change if the cluster is rebuilt. Furthermore, Kubernetes UIDs are + // UUIDs as standardized by + // [ISO/IEC 9834-8 and ITU-T X.667]. + // Which states: + // + // > If generated according to one of the mechanisms defined in Rec. + // > ITU-T X.667 | ISO/IEC 9834-8, a UUID is either guaranteed to be + // > different from all other UUIDs generated before 3603 A.D., or is + // > extremely likely to be different (depending on the mechanism chosen). + // + // Therefore, UIDs between clusters should be extremely unlikely to + // conflict. + // + // [ISO/IEC 9834-8 and ITU-T X.667]: https://www.itu.int/ITU-T/studygroups/com17/oid.html + K8SClusterUIDKey = attribute.Key("k8s.cluster.uid") + + // K8SContainerNameKey is the attribute Key conforming to the + // "k8s.container.name" semantic conventions. It represents the name of the + // Container from Pod specification, must be unique within a Pod. Container + // runtime usually uses different globally unique name (`container.name`). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "redis" + K8SContainerNameKey = attribute.Key("k8s.container.name") + + // K8SContainerRestartCountKey is the attribute Key conforming to the + // "k8s.container.restart_count" semantic conventions. It represents the number + // of times the container was restarted. This attribute can be used to identify + // a particular container (running or stopped) within a container spec. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: + K8SContainerRestartCountKey = attribute.Key("k8s.container.restart_count") + + // K8SContainerStatusLastTerminatedReasonKey is the attribute Key conforming to + // the "k8s.container.status.last_terminated_reason" semantic conventions. It + // represents the last terminated reason of the Container. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Evicted", "Error" + K8SContainerStatusLastTerminatedReasonKey = attribute.Key("k8s.container.status.last_terminated_reason") + + // K8SContainerStatusReasonKey is the attribute Key conforming to the + // "k8s.container.status.reason" semantic conventions. It represents the reason + // for the container state. Corresponds to the `reason` field of the: + // [K8s ContainerStateWaiting] or [K8s ContainerStateTerminated]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "ContainerCreating", "CrashLoopBackOff", + // "CreateContainerConfigError", "ErrImagePull", "ImagePullBackOff", + // "OOMKilled", "Completed", "Error", "ContainerCannotRun" + // + // [K8s ContainerStateWaiting]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#containerstatewaiting-v1-core + // [K8s ContainerStateTerminated]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#containerstateterminated-v1-core + K8SContainerStatusReasonKey = attribute.Key("k8s.container.status.reason") + + // K8SContainerStatusStateKey is the attribute Key conforming to the + // "k8s.container.status.state" semantic conventions. It represents the state of + // the container. [K8s ContainerState]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "terminated", "running", "waiting" + // + // [K8s ContainerState]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#containerstate-v1-core + K8SContainerStatusStateKey = attribute.Key("k8s.container.status.state") + + // K8SCronJobNameKey is the attribute Key conforming to the "k8s.cronjob.name" + // semantic conventions. It represents the name of the CronJob. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "opentelemetry" + K8SCronJobNameKey = attribute.Key("k8s.cronjob.name") + + // K8SCronJobUIDKey is the attribute Key conforming to the "k8s.cronjob.uid" + // semantic conventions. It represents the UID of the CronJob. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + K8SCronJobUIDKey = attribute.Key("k8s.cronjob.uid") + + // K8SDaemonSetNameKey is the attribute Key conforming to the + // "k8s.daemonset.name" semantic conventions. It represents the name of the + // DaemonSet. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "opentelemetry" + K8SDaemonSetNameKey = attribute.Key("k8s.daemonset.name") + + // K8SDaemonSetUIDKey is the attribute Key conforming to the "k8s.daemonset.uid" + // semantic conventions. It represents the UID of the DaemonSet. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + K8SDaemonSetUIDKey = attribute.Key("k8s.daemonset.uid") + + // K8SDeploymentNameKey is the attribute Key conforming to the + // "k8s.deployment.name" semantic conventions. It represents the name of the + // Deployment. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "opentelemetry" + K8SDeploymentNameKey = attribute.Key("k8s.deployment.name") + + // K8SDeploymentUIDKey is the attribute Key conforming to the + // "k8s.deployment.uid" semantic conventions. It represents the UID of the + // Deployment. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + K8SDeploymentUIDKey = attribute.Key("k8s.deployment.uid") + + // K8SHPAMetricTypeKey is the attribute Key conforming to the + // "k8s.hpa.metric.type" semantic conventions. It represents the type of metric + // source for the horizontal pod autoscaler. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Resource", "ContainerResource" + // Note: This attribute reflects the `type` field of spec.metrics[] in the HPA. + K8SHPAMetricTypeKey = attribute.Key("k8s.hpa.metric.type") + + // K8SHPANameKey is the attribute Key conforming to the "k8s.hpa.name" semantic + // conventions. It represents the name of the horizontal pod autoscaler. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "opentelemetry" + K8SHPANameKey = attribute.Key("k8s.hpa.name") + + // K8SHPAScaletargetrefAPIVersionKey is the attribute Key conforming to the + // "k8s.hpa.scaletargetref.api_version" semantic conventions. It represents the + // API version of the target resource to scale for the HorizontalPodAutoscaler. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "apps/v1", "autoscaling/v2" + // Note: This maps to the `apiVersion` field in the `scaleTargetRef` of the HPA + // spec. + K8SHPAScaletargetrefAPIVersionKey = attribute.Key("k8s.hpa.scaletargetref.api_version") + + // K8SHPAScaletargetrefKindKey is the attribute Key conforming to the + // "k8s.hpa.scaletargetref.kind" semantic conventions. It represents the kind of + // the target resource to scale for the HorizontalPodAutoscaler. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Deployment", "StatefulSet" + // Note: This maps to the `kind` field in the `scaleTargetRef` of the HPA spec. + K8SHPAScaletargetrefKindKey = attribute.Key("k8s.hpa.scaletargetref.kind") + + // K8SHPAScaletargetrefNameKey is the attribute Key conforming to the + // "k8s.hpa.scaletargetref.name" semantic conventions. It represents the name of + // the target resource to scale for the HorizontalPodAutoscaler. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-deployment", "my-statefulset" + // Note: This maps to the `name` field in the `scaleTargetRef` of the HPA spec. + K8SHPAScaletargetrefNameKey = attribute.Key("k8s.hpa.scaletargetref.name") + + // K8SHPAUIDKey is the attribute Key conforming to the "k8s.hpa.uid" semantic + // conventions. It represents the UID of the horizontal pod autoscaler. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + K8SHPAUIDKey = attribute.Key("k8s.hpa.uid") + + // K8SHugepageSizeKey is the attribute Key conforming to the "k8s.hugepage.size" + // semantic conventions. It represents the size (identifier) of the K8s huge + // page. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2Mi" + K8SHugepageSizeKey = attribute.Key("k8s.hugepage.size") + + // K8SJobNameKey is the attribute Key conforming to the "k8s.job.name" semantic + // conventions. It represents the name of the Job. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "opentelemetry" + K8SJobNameKey = attribute.Key("k8s.job.name") + + // K8SJobUIDKey is the attribute Key conforming to the "k8s.job.uid" semantic + // conventions. It represents the UID of the Job. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + K8SJobUIDKey = attribute.Key("k8s.job.uid") + + // K8SNamespaceNameKey is the attribute Key conforming to the + // "k8s.namespace.name" semantic conventions. It represents the name of the + // namespace that the pod is running in. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "default" + K8SNamespaceNameKey = attribute.Key("k8s.namespace.name") + + // K8SNamespacePhaseKey is the attribute Key conforming to the + // "k8s.namespace.phase" semantic conventions. It represents the phase of the + // K8s namespace. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "active", "terminating" + // Note: This attribute aligns with the `phase` field of the + // [K8s NamespaceStatus] + // + // [K8s NamespaceStatus]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#namespacestatus-v1-core + K8SNamespacePhaseKey = attribute.Key("k8s.namespace.phase") + + // K8SNodeConditionStatusKey is the attribute Key conforming to the + // "k8s.node.condition.status" semantic conventions. It represents the status of + // the condition, one of True, False, Unknown. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "true", "false", "unknown" + // Note: This attribute aligns with the `status` field of the + // [NodeCondition] + // + // [NodeCondition]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#nodecondition-v1-core + K8SNodeConditionStatusKey = attribute.Key("k8s.node.condition.status") + + // K8SNodeConditionTypeKey is the attribute Key conforming to the + // "k8s.node.condition.type" semantic conventions. It represents the condition + // type of a K8s Node. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Ready", "DiskPressure" + // Note: K8s Node conditions as described + // by [K8s documentation]. + // + // This attribute aligns with the `type` field of the + // [NodeCondition] + // + // The set of possible values is not limited to those listed here. Managed + // Kubernetes environments, + // or custom controllers MAY introduce additional node condition types. + // When this occurs, the exact value as reported by the Kubernetes API SHOULD be + // used. + // + // [K8s documentation]: https://v1-32.docs.kubernetes.io/docs/reference/node/node-status/#condition + // [NodeCondition]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#nodecondition-v1-core + K8SNodeConditionTypeKey = attribute.Key("k8s.node.condition.type") + + // K8SNodeNameKey is the attribute Key conforming to the "k8s.node.name" + // semantic conventions. It represents the name of the Node. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "node-1" + K8SNodeNameKey = attribute.Key("k8s.node.name") + + // K8SNodeUIDKey is the attribute Key conforming to the "k8s.node.uid" semantic + // conventions. It represents the UID of the Node. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "1eb3a0c6-0477-4080-a9cb-0cb7db65c6a2" + K8SNodeUIDKey = attribute.Key("k8s.node.uid") + + // K8SPodHostnameKey is the attribute Key conforming to the "k8s.pod.hostname" + // semantic conventions. It represents the specifies the hostname of the Pod. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "collector-gateway" + // Note: The K8s Pod spec has an optional hostname field, which can be used to + // specify a hostname. + // Refer to [K8s docs] + // for more information about this field. + // + // This attribute aligns with the `hostname` field of the + // [K8s PodSpec]. + // + // [K8s docs]: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-hostname-and-subdomain-field + // [K8s PodSpec]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#podspec-v1-core + K8SPodHostnameKey = attribute.Key("k8s.pod.hostname") + + // K8SPodIPKey is the attribute Key conforming to the "k8s.pod.ip" semantic + // conventions. It represents the IP address allocated to the Pod. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "172.18.0.2" + // Note: This attribute aligns with the `podIP` field of the + // [K8s PodStatus]. + // + // [K8s PodStatus]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#podstatus-v1-core + K8SPodIPKey = attribute.Key("k8s.pod.ip") + + // K8SPodNameKey is the attribute Key conforming to the "k8s.pod.name" semantic + // conventions. It represents the name of the Pod. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "opentelemetry-pod-autoconf" + K8SPodNameKey = attribute.Key("k8s.pod.name") + + // K8SPodStartTimeKey is the attribute Key conforming to the + // "k8s.pod.start_time" semantic conventions. It represents the start timestamp + // of the Pod. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "2025-12-04T08:41:03Z" + // Note: Date and time at which the object was acknowledged by the Kubelet. + // This is before the Kubelet pulled the container image(s) for the pod. + // + // This attribute aligns with the `startTime` field of the + // [K8s PodStatus], + // in ISO 8601 (RFC 3339 compatible) format. + // + // [K8s PodStatus]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#podstatus-v1-core + K8SPodStartTimeKey = attribute.Key("k8s.pod.start_time") + + // K8SPodStatusPhaseKey is the attribute Key conforming to the + // "k8s.pod.status.phase" semantic conventions. It represents the phase for the + // pod. Corresponds to the `phase` field of the: [K8s PodStatus]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Pending", "Running" + // + // [K8s PodStatus]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.33/#podstatus-v1-core + K8SPodStatusPhaseKey = attribute.Key("k8s.pod.status.phase") + + // K8SPodStatusReasonKey is the attribute Key conforming to the + // "k8s.pod.status.reason" semantic conventions. It represents the reason for + // the pod state. Corresponds to the `reason` field of the: [K8s PodStatus]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Evicted", "NodeAffinity" + // + // [K8s PodStatus]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.33/#podstatus-v1-core + K8SPodStatusReasonKey = attribute.Key("k8s.pod.status.reason") + + // K8SPodUIDKey is the attribute Key conforming to the "k8s.pod.uid" semantic + // conventions. It represents the UID of the Pod. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + K8SPodUIDKey = attribute.Key("k8s.pod.uid") + + // K8SReplicaSetNameKey is the attribute Key conforming to the + // "k8s.replicaset.name" semantic conventions. It represents the name of the + // ReplicaSet. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "opentelemetry" + K8SReplicaSetNameKey = attribute.Key("k8s.replicaset.name") + + // K8SReplicaSetUIDKey is the attribute Key conforming to the + // "k8s.replicaset.uid" semantic conventions. It represents the UID of the + // ReplicaSet. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + K8SReplicaSetUIDKey = attribute.Key("k8s.replicaset.uid") + + // K8SReplicationControllerNameKey is the attribute Key conforming to the + // "k8s.replicationcontroller.name" semantic conventions. It represents the name + // of the replication controller. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "opentelemetry" + K8SReplicationControllerNameKey = attribute.Key("k8s.replicationcontroller.name") + + // K8SReplicationControllerUIDKey is the attribute Key conforming to the + // "k8s.replicationcontroller.uid" semantic conventions. It represents the UID + // of the replication controller. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + K8SReplicationControllerUIDKey = attribute.Key("k8s.replicationcontroller.uid") + + // K8SResourceQuotaNameKey is the attribute Key conforming to the + // "k8s.resourcequota.name" semantic conventions. It represents the name of the + // resource quota. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "opentelemetry" + K8SResourceQuotaNameKey = attribute.Key("k8s.resourcequota.name") + + // K8SResourceQuotaResourceNameKey is the attribute Key conforming to the + // "k8s.resourcequota.resource_name" semantic conventions. It represents the + // name of the K8s resource a resource quota defines. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "count/replicationcontrollers" + // Note: The value for this attribute can be either the full + // `count/[.]` string (e.g., count/deployments.apps, + // count/pods), or, for certain core Kubernetes resources, just the resource + // name (e.g., pods, services, configmaps). Both forms are supported by + // Kubernetes for object count quotas. See + // [Kubernetes Resource Quotas documentation] for more details. + // + // [Kubernetes Resource Quotas documentation]: https://kubernetes.io/docs/concepts/policy/resource-quotas/#quota-on-object-count + K8SResourceQuotaResourceNameKey = attribute.Key("k8s.resourcequota.resource_name") + + // K8SResourceQuotaUIDKey is the attribute Key conforming to the + // "k8s.resourcequota.uid" semantic conventions. It represents the UID of the + // resource quota. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + K8SResourceQuotaUIDKey = attribute.Key("k8s.resourcequota.uid") + + // K8SStatefulSetNameKey is the attribute Key conforming to the + // "k8s.statefulset.name" semantic conventions. It represents the name of the + // StatefulSet. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "opentelemetry" + K8SStatefulSetNameKey = attribute.Key("k8s.statefulset.name") + + // K8SStatefulSetUIDKey is the attribute Key conforming to the + // "k8s.statefulset.uid" semantic conventions. It represents the UID of the + // StatefulSet. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Alpha + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + K8SStatefulSetUIDKey = attribute.Key("k8s.statefulset.uid") + + // K8SStorageclassNameKey is the attribute Key conforming to the + // "k8s.storageclass.name" semantic conventions. It represents the name of K8s + // [StorageClass] object. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "gold.storageclass.storage.k8s.io" + // + // [StorageClass]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#storageclass-v1-storage-k8s-io + K8SStorageclassNameKey = attribute.Key("k8s.storageclass.name") + + // K8SVolumeNameKey is the attribute Key conforming to the "k8s.volume.name" + // semantic conventions. It represents the name of the K8s volume. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "volume0" + K8SVolumeNameKey = attribute.Key("k8s.volume.name") + + // K8SVolumeTypeKey is the attribute Key conforming to the "k8s.volume.type" + // semantic conventions. It represents the type of the K8s volume. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "emptyDir", "persistentVolumeClaim" + K8SVolumeTypeKey = attribute.Key("k8s.volume.type") +) + +// K8SClusterName returns an attribute KeyValue conforming to the +// "k8s.cluster.name" semantic conventions. It represents the name of the +// cluster. +func K8SClusterName(val string) attribute.KeyValue { + return K8SClusterNameKey.String(val) +} + +// K8SClusterUID returns an attribute KeyValue conforming to the +// "k8s.cluster.uid" semantic conventions. It represents a pseudo-ID for the +// cluster, set to the UID of the `kube-system` namespace. +func K8SClusterUID(val string) attribute.KeyValue { + return K8SClusterUIDKey.String(val) +} + +// K8SContainerName returns an attribute KeyValue conforming to the +// "k8s.container.name" semantic conventions. It represents the name of the +// Container from Pod specification, must be unique within a Pod. Container +// runtime usually uses different globally unique name (`container.name`). +func K8SContainerName(val string) attribute.KeyValue { + return K8SContainerNameKey.String(val) +} + +// K8SContainerRestartCount returns an attribute KeyValue conforming to the +// "k8s.container.restart_count" semantic conventions. It represents the number +// of times the container was restarted. This attribute can be used to identify a +// particular container (running or stopped) within a container spec. +func K8SContainerRestartCount(val int) attribute.KeyValue { + return K8SContainerRestartCountKey.Int(val) +} + +// K8SContainerStatusLastTerminatedReason returns an attribute KeyValue +// conforming to the "k8s.container.status.last_terminated_reason" semantic +// conventions. It represents the last terminated reason of the Container. +func K8SContainerStatusLastTerminatedReason(val string) attribute.KeyValue { + return K8SContainerStatusLastTerminatedReasonKey.String(val) +} + +// K8SCronJobAnnotation returns an attribute KeyValue conforming to the +// "k8s.cronjob.annotation" semantic conventions. It represents the cronjob +// annotation placed on the CronJob, the `` being the annotation name, the +// value being the annotation value. +func K8SCronJobAnnotation(key string, val string) attribute.KeyValue { + return attribute.String("k8s.cronjob.annotation."+key, val) +} + +// K8SCronJobLabel returns an attribute KeyValue conforming to the +// "k8s.cronjob.label" semantic conventions. It represents the label placed on +// the CronJob, the `` being the label name, the value being the label +// value. +func K8SCronJobLabel(key string, val string) attribute.KeyValue { + return attribute.String("k8s.cronjob.label."+key, val) +} + +// K8SCronJobName returns an attribute KeyValue conforming to the +// "k8s.cronjob.name" semantic conventions. It represents the name of the +// CronJob. +func K8SCronJobName(val string) attribute.KeyValue { + return K8SCronJobNameKey.String(val) +} + +// K8SCronJobUID returns an attribute KeyValue conforming to the +// "k8s.cronjob.uid" semantic conventions. It represents the UID of the CronJob. +func K8SCronJobUID(val string) attribute.KeyValue { + return K8SCronJobUIDKey.String(val) +} + +// K8SDaemonSetAnnotation returns an attribute KeyValue conforming to the +// "k8s.daemonset.annotation" semantic conventions. It represents the annotation +// placed on the DaemonSet, the `` being the annotation name, the value +// being the annotation value, even if the value is empty. +func K8SDaemonSetAnnotation(key string, val string) attribute.KeyValue { + return attribute.String("k8s.daemonset.annotation."+key, val) +} + +// K8SDaemonSetLabel returns an attribute KeyValue conforming to the +// "k8s.daemonset.label" semantic conventions. It represents the label placed on +// the DaemonSet, the `` being the label name, the value being the label +// value, even if the value is empty. +func K8SDaemonSetLabel(key string, val string) attribute.KeyValue { + return attribute.String("k8s.daemonset.label."+key, val) +} + +// K8SDaemonSetName returns an attribute KeyValue conforming to the +// "k8s.daemonset.name" semantic conventions. It represents the name of the +// DaemonSet. +func K8SDaemonSetName(val string) attribute.KeyValue { + return K8SDaemonSetNameKey.String(val) +} + +// K8SDaemonSetUID returns an attribute KeyValue conforming to the +// "k8s.daemonset.uid" semantic conventions. It represents the UID of the +// DaemonSet. +func K8SDaemonSetUID(val string) attribute.KeyValue { + return K8SDaemonSetUIDKey.String(val) +} + +// K8SDeploymentAnnotation returns an attribute KeyValue conforming to the +// "k8s.deployment.annotation" semantic conventions. It represents the annotation +// placed on the Deployment, the `` being the annotation name, the value +// being the annotation value, even if the value is empty. +func K8SDeploymentAnnotation(key string, val string) attribute.KeyValue { + return attribute.String("k8s.deployment.annotation."+key, val) +} + +// K8SDeploymentLabel returns an attribute KeyValue conforming to the +// "k8s.deployment.label" semantic conventions. It represents the label placed on +// the Deployment, the `` being the label name, the value being the label +// value, even if the value is empty. +func K8SDeploymentLabel(key string, val string) attribute.KeyValue { + return attribute.String("k8s.deployment.label."+key, val) +} + +// K8SDeploymentName returns an attribute KeyValue conforming to the +// "k8s.deployment.name" semantic conventions. It represents the name of the +// Deployment. +func K8SDeploymentName(val string) attribute.KeyValue { + return K8SDeploymentNameKey.String(val) +} + +// K8SDeploymentUID returns an attribute KeyValue conforming to the +// "k8s.deployment.uid" semantic conventions. It represents the UID of the +// Deployment. +func K8SDeploymentUID(val string) attribute.KeyValue { + return K8SDeploymentUIDKey.String(val) +} + +// K8SHPAMetricType returns an attribute KeyValue conforming to the +// "k8s.hpa.metric.type" semantic conventions. It represents the type of metric +// source for the horizontal pod autoscaler. +func K8SHPAMetricType(val string) attribute.KeyValue { + return K8SHPAMetricTypeKey.String(val) +} + +// K8SHPAName returns an attribute KeyValue conforming to the "k8s.hpa.name" +// semantic conventions. It represents the name of the horizontal pod autoscaler. +func K8SHPAName(val string) attribute.KeyValue { + return K8SHPANameKey.String(val) +} + +// K8SHPAScaletargetrefAPIVersion returns an attribute KeyValue conforming to the +// "k8s.hpa.scaletargetref.api_version" semantic conventions. It represents the +// API version of the target resource to scale for the HorizontalPodAutoscaler. +func K8SHPAScaletargetrefAPIVersion(val string) attribute.KeyValue { + return K8SHPAScaletargetrefAPIVersionKey.String(val) +} + +// K8SHPAScaletargetrefKind returns an attribute KeyValue conforming to the +// "k8s.hpa.scaletargetref.kind" semantic conventions. It represents the kind of +// the target resource to scale for the HorizontalPodAutoscaler. +func K8SHPAScaletargetrefKind(val string) attribute.KeyValue { + return K8SHPAScaletargetrefKindKey.String(val) +} + +// K8SHPAScaletargetrefName returns an attribute KeyValue conforming to the +// "k8s.hpa.scaletargetref.name" semantic conventions. It represents the name of +// the target resource to scale for the HorizontalPodAutoscaler. +func K8SHPAScaletargetrefName(val string) attribute.KeyValue { + return K8SHPAScaletargetrefNameKey.String(val) +} + +// K8SHPAUID returns an attribute KeyValue conforming to the "k8s.hpa.uid" +// semantic conventions. It represents the UID of the horizontal pod autoscaler. +func K8SHPAUID(val string) attribute.KeyValue { + return K8SHPAUIDKey.String(val) +} + +// K8SHugepageSize returns an attribute KeyValue conforming to the +// "k8s.hugepage.size" semantic conventions. It represents the size (identifier) +// of the K8s huge page. +func K8SHugepageSize(val string) attribute.KeyValue { + return K8SHugepageSizeKey.String(val) +} + +// K8SJobAnnotation returns an attribute KeyValue conforming to the +// "k8s.job.annotation" semantic conventions. It represents the annotation placed +// on the Job, the `` being the annotation name, the value being the +// annotation value, even if the value is empty. +func K8SJobAnnotation(key string, val string) attribute.KeyValue { + return attribute.String("k8s.job.annotation."+key, val) +} + +// K8SJobLabel returns an attribute KeyValue conforming to the "k8s.job.label" +// semantic conventions. It represents the label placed on the Job, the `` +// being the label name, the value being the label value, even if the value is +// empty. +func K8SJobLabel(key string, val string) attribute.KeyValue { + return attribute.String("k8s.job.label."+key, val) +} + +// K8SJobName returns an attribute KeyValue conforming to the "k8s.job.name" +// semantic conventions. It represents the name of the Job. +func K8SJobName(val string) attribute.KeyValue { + return K8SJobNameKey.String(val) +} + +// K8SJobUID returns an attribute KeyValue conforming to the "k8s.job.uid" +// semantic conventions. It represents the UID of the Job. +func K8SJobUID(val string) attribute.KeyValue { + return K8SJobUIDKey.String(val) +} + +// K8SNamespaceAnnotation returns an attribute KeyValue conforming to the +// "k8s.namespace.annotation" semantic conventions. It represents the annotation +// placed on the Namespace, the `` being the annotation name, the value +// being the annotation value, even if the value is empty. +func K8SNamespaceAnnotation(key string, val string) attribute.KeyValue { + return attribute.String("k8s.namespace.annotation."+key, val) +} + +// K8SNamespaceLabel returns an attribute KeyValue conforming to the +// "k8s.namespace.label" semantic conventions. It represents the label placed on +// the Namespace, the `` being the label name, the value being the label +// value, even if the value is empty. +func K8SNamespaceLabel(key string, val string) attribute.KeyValue { + return attribute.String("k8s.namespace.label."+key, val) +} + +// K8SNamespaceName returns an attribute KeyValue conforming to the +// "k8s.namespace.name" semantic conventions. It represents the name of the +// namespace that the pod is running in. +func K8SNamespaceName(val string) attribute.KeyValue { + return K8SNamespaceNameKey.String(val) +} + +// K8SNodeAnnotation returns an attribute KeyValue conforming to the +// "k8s.node.annotation" semantic conventions. It represents the annotation +// placed on the Node, the `` being the annotation name, the value being the +// annotation value, even if the value is empty. +func K8SNodeAnnotation(key string, val string) attribute.KeyValue { + return attribute.String("k8s.node.annotation."+key, val) +} + +// K8SNodeLabel returns an attribute KeyValue conforming to the "k8s.node.label" +// semantic conventions. It represents the label placed on the Node, the `` +// being the label name, the value being the label value, even if the value is +// empty. +func K8SNodeLabel(key string, val string) attribute.KeyValue { + return attribute.String("k8s.node.label."+key, val) +} + +// K8SNodeName returns an attribute KeyValue conforming to the "k8s.node.name" +// semantic conventions. It represents the name of the Node. +func K8SNodeName(val string) attribute.KeyValue { + return K8SNodeNameKey.String(val) +} + +// K8SNodeUID returns an attribute KeyValue conforming to the "k8s.node.uid" +// semantic conventions. It represents the UID of the Node. +func K8SNodeUID(val string) attribute.KeyValue { + return K8SNodeUIDKey.String(val) +} + +// K8SPodAnnotation returns an attribute KeyValue conforming to the +// "k8s.pod.annotation" semantic conventions. It represents the annotation placed +// on the Pod, the `` being the annotation name, the value being the +// annotation value. +func K8SPodAnnotation(key string, val string) attribute.KeyValue { + return attribute.String("k8s.pod.annotation."+key, val) +} + +// K8SPodHostname returns an attribute KeyValue conforming to the +// "k8s.pod.hostname" semantic conventions. It represents the specifies the +// hostname of the Pod. +func K8SPodHostname(val string) attribute.KeyValue { + return K8SPodHostnameKey.String(val) +} + +// K8SPodIP returns an attribute KeyValue conforming to the "k8s.pod.ip" semantic +// conventions. It represents the IP address allocated to the Pod. +func K8SPodIP(val string) attribute.KeyValue { + return K8SPodIPKey.String(val) +} + +// K8SPodLabel returns an attribute KeyValue conforming to the "k8s.pod.label" +// semantic conventions. It represents the label placed on the Pod, the `` +// being the label name, the value being the label value. +func K8SPodLabel(key string, val string) attribute.KeyValue { + return attribute.String("k8s.pod.label."+key, val) +} + +// K8SPodName returns an attribute KeyValue conforming to the "k8s.pod.name" +// semantic conventions. It represents the name of the Pod. +func K8SPodName(val string) attribute.KeyValue { + return K8SPodNameKey.String(val) +} + +// K8SPodStartTime returns an attribute KeyValue conforming to the +// "k8s.pod.start_time" semantic conventions. It represents the start timestamp +// of the Pod. +func K8SPodStartTime(val string) attribute.KeyValue { + return K8SPodStartTimeKey.String(val) +} + +// K8SPodUID returns an attribute KeyValue conforming to the "k8s.pod.uid" +// semantic conventions. It represents the UID of the Pod. +func K8SPodUID(val string) attribute.KeyValue { + return K8SPodUIDKey.String(val) +} + +// K8SReplicaSetAnnotation returns an attribute KeyValue conforming to the +// "k8s.replicaset.annotation" semantic conventions. It represents the annotation +// placed on the ReplicaSet, the `` being the annotation name, the value +// being the annotation value, even if the value is empty. +func K8SReplicaSetAnnotation(key string, val string) attribute.KeyValue { + return attribute.String("k8s.replicaset.annotation."+key, val) +} + +// K8SReplicaSetLabel returns an attribute KeyValue conforming to the +// "k8s.replicaset.label" semantic conventions. It represents the label placed on +// the ReplicaSet, the `` being the label name, the value being the label +// value, even if the value is empty. +func K8SReplicaSetLabel(key string, val string) attribute.KeyValue { + return attribute.String("k8s.replicaset.label."+key, val) +} + +// K8SReplicaSetName returns an attribute KeyValue conforming to the +// "k8s.replicaset.name" semantic conventions. It represents the name of the +// ReplicaSet. +func K8SReplicaSetName(val string) attribute.KeyValue { + return K8SReplicaSetNameKey.String(val) +} + +// K8SReplicaSetUID returns an attribute KeyValue conforming to the +// "k8s.replicaset.uid" semantic conventions. It represents the UID of the +// ReplicaSet. +func K8SReplicaSetUID(val string) attribute.KeyValue { + return K8SReplicaSetUIDKey.String(val) +} + +// K8SReplicationControllerName returns an attribute KeyValue conforming to the +// "k8s.replicationcontroller.name" semantic conventions. It represents the name +// of the replication controller. +func K8SReplicationControllerName(val string) attribute.KeyValue { + return K8SReplicationControllerNameKey.String(val) +} + +// K8SReplicationControllerUID returns an attribute KeyValue conforming to the +// "k8s.replicationcontroller.uid" semantic conventions. It represents the UID of +// the replication controller. +func K8SReplicationControllerUID(val string) attribute.KeyValue { + return K8SReplicationControllerUIDKey.String(val) +} + +// K8SResourceQuotaName returns an attribute KeyValue conforming to the +// "k8s.resourcequota.name" semantic conventions. It represents the name of the +// resource quota. +func K8SResourceQuotaName(val string) attribute.KeyValue { + return K8SResourceQuotaNameKey.String(val) +} + +// K8SResourceQuotaResourceName returns an attribute KeyValue conforming to the +// "k8s.resourcequota.resource_name" semantic conventions. It represents the name +// of the K8s resource a resource quota defines. +func K8SResourceQuotaResourceName(val string) attribute.KeyValue { + return K8SResourceQuotaResourceNameKey.String(val) +} + +// K8SResourceQuotaUID returns an attribute KeyValue conforming to the +// "k8s.resourcequota.uid" semantic conventions. It represents the UID of the +// resource quota. +func K8SResourceQuotaUID(val string) attribute.KeyValue { + return K8SResourceQuotaUIDKey.String(val) +} + +// K8SStatefulSetAnnotation returns an attribute KeyValue conforming to the +// "k8s.statefulset.annotation" semantic conventions. It represents the +// annotation placed on the StatefulSet, the `` being the annotation name, +// the value being the annotation value, even if the value is empty. +func K8SStatefulSetAnnotation(key string, val string) attribute.KeyValue { + return attribute.String("k8s.statefulset.annotation."+key, val) +} + +// K8SStatefulSetLabel returns an attribute KeyValue conforming to the +// "k8s.statefulset.label" semantic conventions. It represents the label placed +// on the StatefulSet, the `` being the label name, the value being the +// label value, even if the value is empty. +func K8SStatefulSetLabel(key string, val string) attribute.KeyValue { + return attribute.String("k8s.statefulset.label."+key, val) +} + +// K8SStatefulSetName returns an attribute KeyValue conforming to the +// "k8s.statefulset.name" semantic conventions. It represents the name of the +// StatefulSet. +func K8SStatefulSetName(val string) attribute.KeyValue { + return K8SStatefulSetNameKey.String(val) +} + +// K8SStatefulSetUID returns an attribute KeyValue conforming to the +// "k8s.statefulset.uid" semantic conventions. It represents the UID of the +// StatefulSet. +func K8SStatefulSetUID(val string) attribute.KeyValue { + return K8SStatefulSetUIDKey.String(val) +} + +// K8SStorageclassName returns an attribute KeyValue conforming to the +// "k8s.storageclass.name" semantic conventions. It represents the name of K8s +// [StorageClass] object. +// +// [StorageClass]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#storageclass-v1-storage-k8s-io +func K8SStorageclassName(val string) attribute.KeyValue { + return K8SStorageclassNameKey.String(val) +} + +// K8SVolumeName returns an attribute KeyValue conforming to the +// "k8s.volume.name" semantic conventions. It represents the name of the K8s +// volume. +func K8SVolumeName(val string) attribute.KeyValue { + return K8SVolumeNameKey.String(val) +} + +// Enum values for k8s.container.status.reason +var ( + // The container is being created. + // Stability: development + K8SContainerStatusReasonContainerCreating = K8SContainerStatusReasonKey.String("ContainerCreating") + // The container is in a crash loop back off state. + // Stability: development + K8SContainerStatusReasonCrashLoopBackOff = K8SContainerStatusReasonKey.String("CrashLoopBackOff") + // There was an error creating the container configuration. + // Stability: development + K8SContainerStatusReasonCreateContainerConfigError = K8SContainerStatusReasonKey.String("CreateContainerConfigError") + // There was an error pulling the container image. + // Stability: development + K8SContainerStatusReasonErrImagePull = K8SContainerStatusReasonKey.String("ErrImagePull") + // The container image pull is in back off state. + // Stability: development + K8SContainerStatusReasonImagePullBackOff = K8SContainerStatusReasonKey.String("ImagePullBackOff") + // The container was killed due to out of memory. + // Stability: development + K8SContainerStatusReasonOomKilled = K8SContainerStatusReasonKey.String("OOMKilled") + // The container has completed execution. + // Stability: development + K8SContainerStatusReasonCompleted = K8SContainerStatusReasonKey.String("Completed") + // There was an error with the container. + // Stability: development + K8SContainerStatusReasonError = K8SContainerStatusReasonKey.String("Error") + // The container cannot run. + // Stability: development + K8SContainerStatusReasonContainerCannotRun = K8SContainerStatusReasonKey.String("ContainerCannotRun") +) + +// Enum values for k8s.container.status.state +var ( + // The container has terminated. + // Stability: development + K8SContainerStatusStateTerminated = K8SContainerStatusStateKey.String("terminated") + // The container is running. + // Stability: development + K8SContainerStatusStateRunning = K8SContainerStatusStateKey.String("running") + // The container is waiting. + // Stability: development + K8SContainerStatusStateWaiting = K8SContainerStatusStateKey.String("waiting") +) + +// Enum values for k8s.namespace.phase +var ( + // Active namespace phase as described by [K8s API] + // Stability: development + // + // [K8s API]: https://pkg.go.dev/k8s.io/api@v0.31.3/core/v1#NamespacePhase + K8SNamespacePhaseActive = K8SNamespacePhaseKey.String("active") + // Terminating namespace phase as described by [K8s API] + // Stability: development + // + // [K8s API]: https://pkg.go.dev/k8s.io/api@v0.31.3/core/v1#NamespacePhase + K8SNamespacePhaseTerminating = K8SNamespacePhaseKey.String("terminating") +) + +// Enum values for k8s.node.condition.status +var ( + // condition_true + // Stability: development + K8SNodeConditionStatusConditionTrue = K8SNodeConditionStatusKey.String("true") + // condition_false + // Stability: development + K8SNodeConditionStatusConditionFalse = K8SNodeConditionStatusKey.String("false") + // condition_unknown + // Stability: development + K8SNodeConditionStatusConditionUnknown = K8SNodeConditionStatusKey.String("unknown") +) + +// Enum values for k8s.node.condition.type +var ( + // The node is healthy and ready to accept pods + // Stability: development + K8SNodeConditionTypeReady = K8SNodeConditionTypeKey.String("Ready") + // Pressure exists on the disk size—that is, if the disk capacity is low + // Stability: development + K8SNodeConditionTypeDiskPressure = K8SNodeConditionTypeKey.String("DiskPressure") + // Pressure exists on the node memory—that is, if the node memory is low + // Stability: development + K8SNodeConditionTypeMemoryPressure = K8SNodeConditionTypeKey.String("MemoryPressure") + // Pressure exists on the processes—that is, if there are too many processes + // on the node + // Stability: development + K8SNodeConditionTypePIDPressure = K8SNodeConditionTypeKey.String("PIDPressure") + // The network for the node is not correctly configured + // Stability: development + K8SNodeConditionTypeNetworkUnavailable = K8SNodeConditionTypeKey.String("NetworkUnavailable") +) + +// Enum values for k8s.pod.status.phase +var ( + // The pod has been accepted by the system, but one or more of the containers + // has not been started. This includes time before being bound to a node, as + // well as time spent pulling images onto the host. + // + // Stability: development + K8SPodStatusPhasePending = K8SPodStatusPhaseKey.String("Pending") + // The pod has been bound to a node and all of the containers have been started. + // At least one container is still running or is in the process of being + // restarted. + // + // Stability: development + K8SPodStatusPhaseRunning = K8SPodStatusPhaseKey.String("Running") + // All containers in the pod have voluntarily terminated with a container exit + // code of 0, and the system is not going to restart any of these containers. + // + // Stability: development + K8SPodStatusPhaseSucceeded = K8SPodStatusPhaseKey.String("Succeeded") + // All containers in the pod have terminated, and at least one container has + // terminated in a failure (exited with a non-zero exit code or was stopped by + // the system). + // + // Stability: development + K8SPodStatusPhaseFailed = K8SPodStatusPhaseKey.String("Failed") + // For some reason the state of the pod could not be obtained, typically due to + // an error in communicating with the host of the pod. + // + // Stability: development + K8SPodStatusPhaseUnknown = K8SPodStatusPhaseKey.String("Unknown") +) + +// Enum values for k8s.pod.status.reason +var ( + // The pod is evicted. + // Stability: development + K8SPodStatusReasonEvicted = K8SPodStatusReasonKey.String("Evicted") + // The pod is in a status because of its node affinity + // Stability: development + K8SPodStatusReasonNodeAffinity = K8SPodStatusReasonKey.String("NodeAffinity") + // The reason on a pod when its state cannot be confirmed as kubelet is + // unresponsive on the node it is (was) running. + // + // Stability: development + K8SPodStatusReasonNodeLost = K8SPodStatusReasonKey.String("NodeLost") + // The node is shutdown + // Stability: development + K8SPodStatusReasonShutdown = K8SPodStatusReasonKey.String("Shutdown") + // The pod was rejected admission to the node because of an error during + // admission that could not be categorized. + // + // Stability: development + K8SPodStatusReasonUnexpectedAdmissionError = K8SPodStatusReasonKey.String("UnexpectedAdmissionError") +) + +// Enum values for k8s.volume.type +var ( + // A [persistentVolumeClaim] volume + // Stability: development + // + // [persistentVolumeClaim]: https://v1-30.docs.kubernetes.io/docs/concepts/storage/volumes/#persistentvolumeclaim + K8SVolumeTypePersistentVolumeClaim = K8SVolumeTypeKey.String("persistentVolumeClaim") + // A [configMap] volume + // Stability: development + // + // [configMap]: https://v1-30.docs.kubernetes.io/docs/concepts/storage/volumes/#configmap + K8SVolumeTypeConfigMap = K8SVolumeTypeKey.String("configMap") + // A [downwardAPI] volume + // Stability: development + // + // [downwardAPI]: https://v1-30.docs.kubernetes.io/docs/concepts/storage/volumes/#downwardapi + K8SVolumeTypeDownwardAPI = K8SVolumeTypeKey.String("downwardAPI") + // An [emptyDir] volume + // Stability: development + // + // [emptyDir]: https://v1-30.docs.kubernetes.io/docs/concepts/storage/volumes/#emptydir + K8SVolumeTypeEmptyDir = K8SVolumeTypeKey.String("emptyDir") + // A [secret] volume + // Stability: development + // + // [secret]: https://v1-30.docs.kubernetes.io/docs/concepts/storage/volumes/#secret + K8SVolumeTypeSecret = K8SVolumeTypeKey.String("secret") + // A [local] volume + // Stability: development + // + // [local]: https://v1-30.docs.kubernetes.io/docs/concepts/storage/volumes/#local + K8SVolumeTypeLocal = K8SVolumeTypeKey.String("local") +) + +// Namespace: log +const ( + // LogFileNameKey is the attribute Key conforming to the "log.file.name" + // semantic conventions. It represents the basename of the file. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "audit.log" + LogFileNameKey = attribute.Key("log.file.name") + + // LogFileNameResolvedKey is the attribute Key conforming to the + // "log.file.name_resolved" semantic conventions. It represents the basename of + // the file, with symlinks resolved. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "uuid.log" + LogFileNameResolvedKey = attribute.Key("log.file.name_resolved") + + // LogFilePathKey is the attribute Key conforming to the "log.file.path" + // semantic conventions. It represents the full path to the file. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/var/log/mysql/audit.log" + LogFilePathKey = attribute.Key("log.file.path") + + // LogFilePathResolvedKey is the attribute Key conforming to the + // "log.file.path_resolved" semantic conventions. It represents the full path to + // the file, with symlinks resolved. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/var/lib/docker/uuid.log" + LogFilePathResolvedKey = attribute.Key("log.file.path_resolved") + + // LogIostreamKey is the attribute Key conforming to the "log.iostream" semantic + // conventions. It represents the stream associated with the log. See below for + // a list of well-known values. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + LogIostreamKey = attribute.Key("log.iostream") + + // LogRecordOriginalKey is the attribute Key conforming to the + // "log.record.original" semantic conventions. It represents the complete + // original Log Record. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "77 <86>1 2015-08-06T21:58:59.694Z 192.168.2.133 inactive - - - + // Something happened", "[INFO] 8/3/24 12:34:56 Something happened" + // Note: This value MAY be added when processing a Log Record which was + // originally transmitted as a string or equivalent data type AND the Body field + // of the Log Record does not contain the same value. (e.g. a syslog or a log + // record read from a file.) + LogRecordOriginalKey = attribute.Key("log.record.original") + + // LogRecordUIDKey is the attribute Key conforming to the "log.record.uid" + // semantic conventions. It represents a unique identifier for the Log Record. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "01ARZ3NDEKTSV4RRFFQ69G5FAV" + // Note: If an id is provided, other log records with the same id will be + // considered duplicates and can be removed safely. This means, that two + // distinguishable log records MUST have different values. + // The id MAY be an + // [Universally Unique Lexicographically Sortable Identifier (ULID)], but other + // identifiers (e.g. UUID) may be used as needed. + // + // [Universally Unique Lexicographically Sortable Identifier (ULID)]: https://github.com/ulid/spec + LogRecordUIDKey = attribute.Key("log.record.uid") +) + +// LogFileName returns an attribute KeyValue conforming to the "log.file.name" +// semantic conventions. It represents the basename of the file. +func LogFileName(val string) attribute.KeyValue { + return LogFileNameKey.String(val) +} + +// LogFileNameResolved returns an attribute KeyValue conforming to the +// "log.file.name_resolved" semantic conventions. It represents the basename of +// the file, with symlinks resolved. +func LogFileNameResolved(val string) attribute.KeyValue { + return LogFileNameResolvedKey.String(val) +} + +// LogFilePath returns an attribute KeyValue conforming to the "log.file.path" +// semantic conventions. It represents the full path to the file. +func LogFilePath(val string) attribute.KeyValue { + return LogFilePathKey.String(val) +} + +// LogFilePathResolved returns an attribute KeyValue conforming to the +// "log.file.path_resolved" semantic conventions. It represents the full path to +// the file, with symlinks resolved. +func LogFilePathResolved(val string) attribute.KeyValue { + return LogFilePathResolvedKey.String(val) +} + +// LogRecordOriginal returns an attribute KeyValue conforming to the +// "log.record.original" semantic conventions. It represents the complete +// original Log Record. +func LogRecordOriginal(val string) attribute.KeyValue { + return LogRecordOriginalKey.String(val) +} + +// LogRecordUID returns an attribute KeyValue conforming to the "log.record.uid" +// semantic conventions. It represents a unique identifier for the Log Record. +func LogRecordUID(val string) attribute.KeyValue { + return LogRecordUIDKey.String(val) +} + +// Enum values for log.iostream +var ( + // Logs from stdout stream + // Stability: development + LogIostreamStdout = LogIostreamKey.String("stdout") + // Events from stderr stream + // Stability: development + LogIostreamStderr = LogIostreamKey.String("stderr") +) + +// Namespace: mainframe +const ( + // MainframeLparNameKey is the attribute Key conforming to the + // "mainframe.lpar.name" semantic conventions. It represents the name of the + // logical partition that hosts a systems with a mainframe operating system. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "LPAR01" + MainframeLparNameKey = attribute.Key("mainframe.lpar.name") +) + +// MainframeLparName returns an attribute KeyValue conforming to the +// "mainframe.lpar.name" semantic conventions. It represents the name of the +// logical partition that hosts a systems with a mainframe operating system. +func MainframeLparName(val string) attribute.KeyValue { + return MainframeLparNameKey.String(val) +} + +// Namespace: mcp +const ( + // McpMethodNameKey is the attribute Key conforming to the "mcp.method.name" + // semantic conventions. It represents the name of the request or notification + // method. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + McpMethodNameKey = attribute.Key("mcp.method.name") + + // McpProtocolVersionKey is the attribute Key conforming to the + // "mcp.protocol.version" semantic conventions. It represents the [version] of + // the Model Context Protocol used. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2025-06-18" + // + // [version]: https://modelcontextprotocol.io/specification/versioning + McpProtocolVersionKey = attribute.Key("mcp.protocol.version") + + // McpResourceURIKey is the attribute Key conforming to the "mcp.resource.uri" + // semantic conventions. It represents the value of the resource uri. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "postgres://database/customers/schema", + // "file:///home/user/documents/report.pdf" + // Note: This is a URI of the resource provided in the following requests or + // notifications: `resources/read`, `resources/subscribe`, + // `resources/unsubscribe`, or `notifications/resources/updated`. + McpResourceURIKey = attribute.Key("mcp.resource.uri") + + // McpSessionIDKey is the attribute Key conforming to the "mcp.session.id" + // semantic conventions. It represents the identifies [MCP session]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "191c4850af6c49e08843a3f6c80e5046" + // + // [MCP session]: https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#session-management + McpSessionIDKey = attribute.Key("mcp.session.id") +) + +// McpProtocolVersion returns an attribute KeyValue conforming to the +// "mcp.protocol.version" semantic conventions. It represents the [version] of +// the Model Context Protocol used. +// +// [version]: https://modelcontextprotocol.io/specification/versioning +func McpProtocolVersion(val string) attribute.KeyValue { + return McpProtocolVersionKey.String(val) +} + +// McpResourceURI returns an attribute KeyValue conforming to the +// "mcp.resource.uri" semantic conventions. It represents the value of the +// resource uri. +func McpResourceURI(val string) attribute.KeyValue { + return McpResourceURIKey.String(val) +} + +// McpSessionID returns an attribute KeyValue conforming to the "mcp.session.id" +// semantic conventions. It represents the identifies [MCP session]. +// +// [MCP session]: https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#session-management +func McpSessionID(val string) attribute.KeyValue { + return McpSessionIDKey.String(val) +} + +// Enum values for mcp.method.name +var ( + // Notification cancelling a previously-issued request. + // + // Stability: development + McpMethodNameNotificationsCancelled = McpMethodNameKey.String("notifications/cancelled") + // Request to initialize the MCP client. + // + // Stability: development + McpMethodNameInitialize = McpMethodNameKey.String("initialize") + // Notification indicating that the MCP client has been initialized. + // + // Stability: development + McpMethodNameNotificationsInitialized = McpMethodNameKey.String("notifications/initialized") + // Notification indicating the progress for a long-running operation. + // + // Stability: development + McpMethodNameNotificationsProgress = McpMethodNameKey.String("notifications/progress") + // Request to check that the other party is still alive. + // + // Stability: development + McpMethodNamePing = McpMethodNameKey.String("ping") + // Request to list resources available on server. + // + // Stability: development + McpMethodNameResourcesList = McpMethodNameKey.String("resources/list") + // Request to list resource templates available on server. + // + // Stability: development + McpMethodNameResourcesTemplatesList = McpMethodNameKey.String("resources/templates/list") + // Request to read a resource. + // + // Stability: development + McpMethodNameResourcesRead = McpMethodNameKey.String("resources/read") + // Notification indicating that the list of resources has changed. + // + // Stability: development + McpMethodNameNotificationsResourcesListChanged = McpMethodNameKey.String("notifications/resources/list_changed") + // Request to subscribe to a resource. + // + // Stability: development + McpMethodNameResourcesSubscribe = McpMethodNameKey.String("resources/subscribe") + // Request to unsubscribe from resource updates. + // + // Stability: development + McpMethodNameResourcesUnsubscribe = McpMethodNameKey.String("resources/unsubscribe") + // Notification indicating that a resource has been updated. + // + // Stability: development + McpMethodNameNotificationsResourcesUpdated = McpMethodNameKey.String("notifications/resources/updated") + // Request to list prompts available on server. + // + // Stability: development + McpMethodNamePromptsList = McpMethodNameKey.String("prompts/list") + // Request to get a prompt. + // + // Stability: development + McpMethodNamePromptsGet = McpMethodNameKey.String("prompts/get") + // Notification indicating that the list of prompts has changed. + // + // Stability: development + McpMethodNameNotificationsPromptsListChanged = McpMethodNameKey.String("notifications/prompts/list_changed") + // Request to list tools available on server. + // + // Stability: development + McpMethodNameToolsList = McpMethodNameKey.String("tools/list") + // Request to call a tool. + // + // Stability: development + McpMethodNameToolsCall = McpMethodNameKey.String("tools/call") + // Notification indicating that the list of tools has changed. + // + // Stability: development + McpMethodNameNotificationsToolsListChanged = McpMethodNameKey.String("notifications/tools/list_changed") + // Request to set the logging level. + // + // Stability: development + McpMethodNameLoggingSetLevel = McpMethodNameKey.String("logging/setLevel") + // Notification indicating that a message has been received. + // + // Stability: development + McpMethodNameNotificationsMessage = McpMethodNameKey.String("notifications/message") + // Request to create a sampling message. + // + // Stability: development + McpMethodNameSamplingCreateMessage = McpMethodNameKey.String("sampling/createMessage") + // Request to complete a prompt. + // + // Stability: development + McpMethodNameCompletionComplete = McpMethodNameKey.String("completion/complete") + // Request to list roots available on server. + // + // Stability: development + McpMethodNameRootsList = McpMethodNameKey.String("roots/list") + // Notification indicating that the list of roots has changed. + // + // Stability: development + McpMethodNameNotificationsRootsListChanged = McpMethodNameKey.String("notifications/roots/list_changed") + // Request from the server to elicit additional information from the user via + // the client + // + // Stability: development + McpMethodNameElicitationCreate = McpMethodNameKey.String("elicitation/create") +) + +// Namespace: messaging +const ( + // MessagingBatchMessageCountKey is the attribute Key conforming to the + // "messaging.batch.message_count" semantic conventions. It represents the + // number of messages sent, received, or processed in the scope of the batching + // operation. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 0, 1, 2 + // Note: Instrumentations SHOULD NOT set `messaging.batch.message_count` on + // spans that operate with a single message. When a messaging client library + // supports both batch and single-message API for the same operation, + // instrumentations SHOULD use `messaging.batch.message_count` for batching APIs + // and SHOULD NOT use it for single-message APIs. + MessagingBatchMessageCountKey = attribute.Key("messaging.batch.message_count") + + // MessagingClientIDKey is the attribute Key conforming to the + // "messaging.client.id" semantic conventions. It represents a unique identifier + // for the client that consumes or produces a message. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "client-5", "myhost@8742@s8083jm" + MessagingClientIDKey = attribute.Key("messaging.client.id") + + // MessagingConsumerGroupNameKey is the attribute Key conforming to the + // "messaging.consumer.group.name" semantic conventions. It represents the name + // of the consumer group with which a consumer is associated. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-group", "indexer" + // Note: Semantic conventions for individual messaging systems SHOULD document + // whether `messaging.consumer.group.name` is applicable and what it means in + // the context of that system. + MessagingConsumerGroupNameKey = attribute.Key("messaging.consumer.group.name") + + // MessagingDestinationAnonymousKey is the attribute Key conforming to the + // "messaging.destination.anonymous" semantic conventions. It represents a + // boolean that is true if the message destination is anonymous (could be + // unnamed or have auto-generated name). + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + MessagingDestinationAnonymousKey = attribute.Key("messaging.destination.anonymous") + + // MessagingDestinationNameKey is the attribute Key conforming to the + // "messaging.destination.name" semantic conventions. It represents the message + // destination name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "MyQueue", "MyTopic" + // Note: Destination name SHOULD uniquely identify a specific queue, topic or + // other entity within the broker. If + // the broker doesn't have such notion, the destination name SHOULD uniquely + // identify the broker. + MessagingDestinationNameKey = attribute.Key("messaging.destination.name") + + // MessagingDestinationPartitionIDKey is the attribute Key conforming to the + // "messaging.destination.partition.id" semantic conventions. It represents the + // identifier of the partition messages are sent to or received from, unique + // within the `messaging.destination.name`. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1 + MessagingDestinationPartitionIDKey = attribute.Key("messaging.destination.partition.id") + + // MessagingDestinationSubscriptionNameKey is the attribute Key conforming to + // the "messaging.destination.subscription.name" semantic conventions. It + // represents the name of the destination subscription from which a message is + // consumed. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "subscription-a" + // Note: Semantic conventions for individual messaging systems SHOULD document + // whether `messaging.destination.subscription.name` is applicable and what it + // means in the context of that system. + MessagingDestinationSubscriptionNameKey = attribute.Key("messaging.destination.subscription.name") + + // MessagingDestinationTemplateKey is the attribute Key conforming to the + // "messaging.destination.template" semantic conventions. It represents the low + // cardinality representation of the messaging destination name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/customers/{customerId}" + // Note: Destination names could be constructed from templates. An example would + // be a destination name involving a user name or product id. Although the + // destination name in this case is of high cardinality, the underlying template + // is of low cardinality and can be effectively used for grouping and + // aggregation. + MessagingDestinationTemplateKey = attribute.Key("messaging.destination.template") + + // MessagingDestinationTemporaryKey is the attribute Key conforming to the + // "messaging.destination.temporary" semantic conventions. It represents a + // boolean that is true if the message destination is temporary and might not + // exist anymore after messages are processed. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + MessagingDestinationTemporaryKey = attribute.Key("messaging.destination.temporary") + + // MessagingEventHubsMessageEnqueuedTimeKey is the attribute Key conforming to + // the "messaging.eventhubs.message.enqueued_time" semantic conventions. It + // represents the UTC epoch seconds at which the message has been accepted and + // stored in the entity. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + MessagingEventHubsMessageEnqueuedTimeKey = attribute.Key("messaging.eventhubs.message.enqueued_time") + + // MessagingGCPPubSubMessageAckDeadlineKey is the attribute Key conforming to + // the "messaging.gcp_pubsub.message.ack_deadline" semantic conventions. It + // represents the ack deadline in seconds set for the modify ack deadline + // request. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + MessagingGCPPubSubMessageAckDeadlineKey = attribute.Key("messaging.gcp_pubsub.message.ack_deadline") + + // MessagingGCPPubSubMessageAckIDKey is the attribute Key conforming to the + // "messaging.gcp_pubsub.message.ack_id" semantic conventions. It represents the + // ack id for a given message. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: ack_id + MessagingGCPPubSubMessageAckIDKey = attribute.Key("messaging.gcp_pubsub.message.ack_id") + + // MessagingGCPPubSubMessageDeliveryAttemptKey is the attribute Key conforming + // to the "messaging.gcp_pubsub.message.delivery_attempt" semantic conventions. + // It represents the delivery attempt for a given message. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + MessagingGCPPubSubMessageDeliveryAttemptKey = attribute.Key("messaging.gcp_pubsub.message.delivery_attempt") + + // MessagingGCPPubSubMessageOrderingKeyKey is the attribute Key conforming to + // the "messaging.gcp_pubsub.message.ordering_key" semantic conventions. It + // represents the ordering key for a given message. If the attribute is not + // present, the message does not have an ordering key. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: ordering_key + MessagingGCPPubSubMessageOrderingKeyKey = attribute.Key("messaging.gcp_pubsub.message.ordering_key") + + // MessagingKafkaMessageKeyKey is the attribute Key conforming to the + // "messaging.kafka.message.key" semantic conventions. It represents the message + // keys in Kafka are used for grouping alike messages to ensure they're + // processed on the same partition. They differ from `messaging.message.id` in + // that they're not unique. If the key is `null`, the attribute MUST NOT be set. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: myKey + // Note: If the key type is not string, it's string representation has to be + // supplied for the attribute. If the key has no unambiguous, canonical string + // form, don't include its value. + MessagingKafkaMessageKeyKey = attribute.Key("messaging.kafka.message.key") + + // MessagingKafkaMessageTombstoneKey is the attribute Key conforming to the + // "messaging.kafka.message.tombstone" semantic conventions. It represents a + // boolean that is true if the message is a tombstone. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + MessagingKafkaMessageTombstoneKey = attribute.Key("messaging.kafka.message.tombstone") + + // MessagingKafkaOffsetKey is the attribute Key conforming to the + // "messaging.kafka.offset" semantic conventions. It represents the offset of a + // record in the corresponding Kafka partition. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + MessagingKafkaOffsetKey = attribute.Key("messaging.kafka.offset") + + // MessagingMessageBodySizeKey is the attribute Key conforming to the + // "messaging.message.body.size" semantic conventions. It represents the size of + // the message body in bytes. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Note: This can refer to both the compressed or uncompressed body size. If + // both sizes are known, the uncompressed + // body size should be used. + MessagingMessageBodySizeKey = attribute.Key("messaging.message.body.size") + + // MessagingMessageConversationIDKey is the attribute Key conforming to the + // "messaging.message.conversation_id" semantic conventions. It represents the + // conversation ID identifying the conversation to which the message belongs, + // represented as a string. Sometimes called "Correlation ID". + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: MyConversationId + MessagingMessageConversationIDKey = attribute.Key("messaging.message.conversation_id") + + // MessagingMessageEnvelopeSizeKey is the attribute Key conforming to the + // "messaging.message.envelope.size" semantic conventions. It represents the + // size of the message body and metadata in bytes. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Note: This can refer to both the compressed or uncompressed size. If both + // sizes are known, the uncompressed + // size should be used. + MessagingMessageEnvelopeSizeKey = attribute.Key("messaging.message.envelope.size") + + // MessagingMessageIDKey is the attribute Key conforming to the + // "messaging.message.id" semantic conventions. It represents a value used by + // the messaging system as an identifier for the message, represented as a + // string. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 452a7c7c7c7048c2f887f61572b18fc2 + MessagingMessageIDKey = attribute.Key("messaging.message.id") + + // MessagingOperationNameKey is the attribute Key conforming to the + // "messaging.operation.name" semantic conventions. It represents the + // system-specific name of the messaging operation. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "ack", "nack", "send" + MessagingOperationNameKey = attribute.Key("messaging.operation.name") + + // MessagingOperationTypeKey is the attribute Key conforming to the + // "messaging.operation.type" semantic conventions. It represents a string + // identifying the type of the messaging operation. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: If a custom value is used, it MUST be of low cardinality. + MessagingOperationTypeKey = attribute.Key("messaging.operation.type") + + // MessagingRabbitMQDestinationRoutingKeyKey is the attribute Key conforming to + // the "messaging.rabbitmq.destination.routing_key" semantic conventions. It + // represents the rabbitMQ message routing key. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: myKey + MessagingRabbitMQDestinationRoutingKeyKey = attribute.Key("messaging.rabbitmq.destination.routing_key") + + // MessagingRabbitMQMessageDeliveryTagKey is the attribute Key conforming to the + // "messaging.rabbitmq.message.delivery_tag" semantic conventions. It represents + // the rabbitMQ message delivery tag. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + MessagingRabbitMQMessageDeliveryTagKey = attribute.Key("messaging.rabbitmq.message.delivery_tag") + + // MessagingRocketMQConsumptionModelKey is the attribute Key conforming to the + // "messaging.rocketmq.consumption_model" semantic conventions. It represents + // the model of message consumption. This only applies to consumer spans. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + MessagingRocketMQConsumptionModelKey = attribute.Key("messaging.rocketmq.consumption_model") + + // MessagingRocketMQMessageDelayTimeLevelKey is the attribute Key conforming to + // the "messaging.rocketmq.message.delay_time_level" semantic conventions. It + // represents the delay time level for delay message, which determines the + // message delay time. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + MessagingRocketMQMessageDelayTimeLevelKey = attribute.Key("messaging.rocketmq.message.delay_time_level") + + // MessagingRocketMQMessageDeliveryTimestampKey is the attribute Key conforming + // to the "messaging.rocketmq.message.delivery_timestamp" semantic conventions. + // It represents the timestamp in milliseconds that the delay message is + // expected to be delivered to consumer. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + MessagingRocketMQMessageDeliveryTimestampKey = attribute.Key("messaging.rocketmq.message.delivery_timestamp") + + // MessagingRocketMQMessageGroupKey is the attribute Key conforming to the + // "messaging.rocketmq.message.group" semantic conventions. It represents the it + // is essential for FIFO message. Messages that belong to the same message group + // are always processed one by one within the same consumer group. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: myMessageGroup + MessagingRocketMQMessageGroupKey = attribute.Key("messaging.rocketmq.message.group") + + // MessagingRocketMQMessageKeysKey is the attribute Key conforming to the + // "messaging.rocketmq.message.keys" semantic conventions. It represents the + // key(s) of message, another way to mark message besides message id. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "keyA", "keyB" + MessagingRocketMQMessageKeysKey = attribute.Key("messaging.rocketmq.message.keys") + + // MessagingRocketMQMessageTagKey is the attribute Key conforming to the + // "messaging.rocketmq.message.tag" semantic conventions. It represents the + // secondary classifier of message besides topic. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: tagA + MessagingRocketMQMessageTagKey = attribute.Key("messaging.rocketmq.message.tag") + + // MessagingRocketMQMessageTypeKey is the attribute Key conforming to the + // "messaging.rocketmq.message.type" semantic conventions. It represents the + // type of message. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + MessagingRocketMQMessageTypeKey = attribute.Key("messaging.rocketmq.message.type") + + // MessagingRocketMQNamespaceKey is the attribute Key conforming to the + // "messaging.rocketmq.namespace" semantic conventions. It represents the + // namespace of RocketMQ resources, resources in different namespaces are + // individual. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: myNamespace + MessagingRocketMQNamespaceKey = attribute.Key("messaging.rocketmq.namespace") + + // MessagingServiceBusDispositionStatusKey is the attribute Key conforming to + // the "messaging.servicebus.disposition_status" semantic conventions. It + // represents the describes the [settlement type]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // + // [settlement type]: https://learn.microsoft.com/azure/service-bus-messaging/message-transfers-locks-settlement#peeklock + MessagingServiceBusDispositionStatusKey = attribute.Key("messaging.servicebus.disposition_status") + + // MessagingServiceBusMessageDeliveryCountKey is the attribute Key conforming to + // the "messaging.servicebus.message.delivery_count" semantic conventions. It + // represents the number of deliveries that have been attempted for this + // message. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + MessagingServiceBusMessageDeliveryCountKey = attribute.Key("messaging.servicebus.message.delivery_count") + + // MessagingServiceBusMessageEnqueuedTimeKey is the attribute Key conforming to + // the "messaging.servicebus.message.enqueued_time" semantic conventions. It + // represents the UTC epoch seconds at which the message has been accepted and + // stored in the entity. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + MessagingServiceBusMessageEnqueuedTimeKey = attribute.Key("messaging.servicebus.message.enqueued_time") + + // MessagingSystemKey is the attribute Key conforming to the "messaging.system" + // semantic conventions. It represents the messaging system as identified by the + // client instrumentation. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: The actual messaging system may differ from the one known by the + // client. For example, when using Kafka client libraries to communicate with + // Azure Event Hubs, the `messaging.system` is set to `kafka` based on the + // instrumentation's best knowledge. + MessagingSystemKey = attribute.Key("messaging.system") +) + +// MessagingBatchMessageCount returns an attribute KeyValue conforming to the +// "messaging.batch.message_count" semantic conventions. It represents the number +// of messages sent, received, or processed in the scope of the batching +// operation. +func MessagingBatchMessageCount(val int) attribute.KeyValue { + return MessagingBatchMessageCountKey.Int(val) +} + +// MessagingClientID returns an attribute KeyValue conforming to the +// "messaging.client.id" semantic conventions. It represents a unique identifier +// for the client that consumes or produces a message. +func MessagingClientID(val string) attribute.KeyValue { + return MessagingClientIDKey.String(val) +} + +// MessagingConsumerGroupName returns an attribute KeyValue conforming to the +// "messaging.consumer.group.name" semantic conventions. It represents the name +// of the consumer group with which a consumer is associated. +func MessagingConsumerGroupName(val string) attribute.KeyValue { + return MessagingConsumerGroupNameKey.String(val) +} + +// MessagingDestinationAnonymous returns an attribute KeyValue conforming to the +// "messaging.destination.anonymous" semantic conventions. It represents a +// boolean that is true if the message destination is anonymous (could be unnamed +// or have auto-generated name). +func MessagingDestinationAnonymous(val bool) attribute.KeyValue { + return MessagingDestinationAnonymousKey.Bool(val) +} + +// MessagingDestinationName returns an attribute KeyValue conforming to the +// "messaging.destination.name" semantic conventions. It represents the message +// destination name. +func MessagingDestinationName(val string) attribute.KeyValue { + return MessagingDestinationNameKey.String(val) +} + +// MessagingDestinationPartitionID returns an attribute KeyValue conforming to +// the "messaging.destination.partition.id" semantic conventions. It represents +// the identifier of the partition messages are sent to or received from, unique +// within the `messaging.destination.name`. +func MessagingDestinationPartitionID(val string) attribute.KeyValue { + return MessagingDestinationPartitionIDKey.String(val) +} + +// MessagingDestinationSubscriptionName returns an attribute KeyValue conforming +// to the "messaging.destination.subscription.name" semantic conventions. It +// represents the name of the destination subscription from which a message is +// consumed. +func MessagingDestinationSubscriptionName(val string) attribute.KeyValue { + return MessagingDestinationSubscriptionNameKey.String(val) +} + +// MessagingDestinationTemplate returns an attribute KeyValue conforming to the +// "messaging.destination.template" semantic conventions. It represents the low +// cardinality representation of the messaging destination name. +func MessagingDestinationTemplate(val string) attribute.KeyValue { + return MessagingDestinationTemplateKey.String(val) +} + +// MessagingDestinationTemporary returns an attribute KeyValue conforming to the +// "messaging.destination.temporary" semantic conventions. It represents a +// boolean that is true if the message destination is temporary and might not +// exist anymore after messages are processed. +func MessagingDestinationTemporary(val bool) attribute.KeyValue { + return MessagingDestinationTemporaryKey.Bool(val) +} + +// MessagingEventHubsMessageEnqueuedTime returns an attribute KeyValue conforming +// to the "messaging.eventhubs.message.enqueued_time" semantic conventions. It +// represents the UTC epoch seconds at which the message has been accepted and +// stored in the entity. +func MessagingEventHubsMessageEnqueuedTime(val int) attribute.KeyValue { + return MessagingEventHubsMessageEnqueuedTimeKey.Int(val) +} + +// MessagingGCPPubSubMessageAckDeadline returns an attribute KeyValue conforming +// to the "messaging.gcp_pubsub.message.ack_deadline" semantic conventions. It +// represents the ack deadline in seconds set for the modify ack deadline +// request. +func MessagingGCPPubSubMessageAckDeadline(val int) attribute.KeyValue { + return MessagingGCPPubSubMessageAckDeadlineKey.Int(val) +} + +// MessagingGCPPubSubMessageAckID returns an attribute KeyValue conforming to the +// "messaging.gcp_pubsub.message.ack_id" semantic conventions. It represents the +// ack id for a given message. +func MessagingGCPPubSubMessageAckID(val string) attribute.KeyValue { + return MessagingGCPPubSubMessageAckIDKey.String(val) +} + +// MessagingGCPPubSubMessageDeliveryAttempt returns an attribute KeyValue +// conforming to the "messaging.gcp_pubsub.message.delivery_attempt" semantic +// conventions. It represents the delivery attempt for a given message. +func MessagingGCPPubSubMessageDeliveryAttempt(val int) attribute.KeyValue { + return MessagingGCPPubSubMessageDeliveryAttemptKey.Int(val) +} + +// MessagingGCPPubSubMessageOrderingKey returns an attribute KeyValue conforming +// to the "messaging.gcp_pubsub.message.ordering_key" semantic conventions. It +// represents the ordering key for a given message. If the attribute is not +// present, the message does not have an ordering key. +func MessagingGCPPubSubMessageOrderingKey(val string) attribute.KeyValue { + return MessagingGCPPubSubMessageOrderingKeyKey.String(val) +} + +// MessagingKafkaMessageKey returns an attribute KeyValue conforming to the +// "messaging.kafka.message.key" semantic conventions. It represents the message +// keys in Kafka are used for grouping alike messages to ensure they're processed +// on the same partition. They differ from `messaging.message.id` in that they're +// not unique. If the key is `null`, the attribute MUST NOT be set. +func MessagingKafkaMessageKey(val string) attribute.KeyValue { + return MessagingKafkaMessageKeyKey.String(val) +} + +// MessagingKafkaMessageTombstone returns an attribute KeyValue conforming to the +// "messaging.kafka.message.tombstone" semantic conventions. It represents a +// boolean that is true if the message is a tombstone. +func MessagingKafkaMessageTombstone(val bool) attribute.KeyValue { + return MessagingKafkaMessageTombstoneKey.Bool(val) +} + +// MessagingKafkaOffset returns an attribute KeyValue conforming to the +// "messaging.kafka.offset" semantic conventions. It represents the offset of a +// record in the corresponding Kafka partition. +func MessagingKafkaOffset(val int) attribute.KeyValue { + return MessagingKafkaOffsetKey.Int(val) +} + +// MessagingMessageBodySize returns an attribute KeyValue conforming to the +// "messaging.message.body.size" semantic conventions. It represents the size of +// the message body in bytes. +func MessagingMessageBodySize(val int) attribute.KeyValue { + return MessagingMessageBodySizeKey.Int(val) +} + +// MessagingMessageConversationID returns an attribute KeyValue conforming to the +// "messaging.message.conversation_id" semantic conventions. It represents the +// conversation ID identifying the conversation to which the message belongs, +// represented as a string. Sometimes called "Correlation ID". +func MessagingMessageConversationID(val string) attribute.KeyValue { + return MessagingMessageConversationIDKey.String(val) +} + +// MessagingMessageEnvelopeSize returns an attribute KeyValue conforming to the +// "messaging.message.envelope.size" semantic conventions. It represents the size +// of the message body and metadata in bytes. +func MessagingMessageEnvelopeSize(val int) attribute.KeyValue { + return MessagingMessageEnvelopeSizeKey.Int(val) +} + +// MessagingMessageID returns an attribute KeyValue conforming to the +// "messaging.message.id" semantic conventions. It represents a value used by the +// messaging system as an identifier for the message, represented as a string. +func MessagingMessageID(val string) attribute.KeyValue { + return MessagingMessageIDKey.String(val) +} + +// MessagingOperationName returns an attribute KeyValue conforming to the +// "messaging.operation.name" semantic conventions. It represents the +// system-specific name of the messaging operation. +func MessagingOperationName(val string) attribute.KeyValue { + return MessagingOperationNameKey.String(val) +} + +// MessagingRabbitMQDestinationRoutingKey returns an attribute KeyValue +// conforming to the "messaging.rabbitmq.destination.routing_key" semantic +// conventions. It represents the rabbitMQ message routing key. +func MessagingRabbitMQDestinationRoutingKey(val string) attribute.KeyValue { + return MessagingRabbitMQDestinationRoutingKeyKey.String(val) +} + +// MessagingRabbitMQMessageDeliveryTag returns an attribute KeyValue conforming +// to the "messaging.rabbitmq.message.delivery_tag" semantic conventions. It +// represents the rabbitMQ message delivery tag. +func MessagingRabbitMQMessageDeliveryTag(val int) attribute.KeyValue { + return MessagingRabbitMQMessageDeliveryTagKey.Int(val) +} + +// MessagingRocketMQMessageDelayTimeLevel returns an attribute KeyValue +// conforming to the "messaging.rocketmq.message.delay_time_level" semantic +// conventions. It represents the delay time level for delay message, which +// determines the message delay time. +func MessagingRocketMQMessageDelayTimeLevel(val int) attribute.KeyValue { + return MessagingRocketMQMessageDelayTimeLevelKey.Int(val) +} + +// MessagingRocketMQMessageDeliveryTimestamp returns an attribute KeyValue +// conforming to the "messaging.rocketmq.message.delivery_timestamp" semantic +// conventions. It represents the timestamp in milliseconds that the delay +// message is expected to be delivered to consumer. +func MessagingRocketMQMessageDeliveryTimestamp(val int) attribute.KeyValue { + return MessagingRocketMQMessageDeliveryTimestampKey.Int(val) +} + +// MessagingRocketMQMessageGroup returns an attribute KeyValue conforming to the +// "messaging.rocketmq.message.group" semantic conventions. It represents the it +// is essential for FIFO message. Messages that belong to the same message group +// are always processed one by one within the same consumer group. +func MessagingRocketMQMessageGroup(val string) attribute.KeyValue { + return MessagingRocketMQMessageGroupKey.String(val) +} + +// MessagingRocketMQMessageKeys returns an attribute KeyValue conforming to the +// "messaging.rocketmq.message.keys" semantic conventions. It represents the +// key(s) of message, another way to mark message besides message id. +func MessagingRocketMQMessageKeys(val ...string) attribute.KeyValue { + return MessagingRocketMQMessageKeysKey.StringSlice(val) +} + +// MessagingRocketMQMessageTag returns an attribute KeyValue conforming to the +// "messaging.rocketmq.message.tag" semantic conventions. It represents the +// secondary classifier of message besides topic. +func MessagingRocketMQMessageTag(val string) attribute.KeyValue { + return MessagingRocketMQMessageTagKey.String(val) +} + +// MessagingRocketMQNamespace returns an attribute KeyValue conforming to the +// "messaging.rocketmq.namespace" semantic conventions. It represents the +// namespace of RocketMQ resources, resources in different namespaces are +// individual. +func MessagingRocketMQNamespace(val string) attribute.KeyValue { + return MessagingRocketMQNamespaceKey.String(val) +} + +// MessagingServiceBusMessageDeliveryCount returns an attribute KeyValue +// conforming to the "messaging.servicebus.message.delivery_count" semantic +// conventions. It represents the number of deliveries that have been attempted +// for this message. +func MessagingServiceBusMessageDeliveryCount(val int) attribute.KeyValue { + return MessagingServiceBusMessageDeliveryCountKey.Int(val) +} + +// MessagingServiceBusMessageEnqueuedTime returns an attribute KeyValue +// conforming to the "messaging.servicebus.message.enqueued_time" semantic +// conventions. It represents the UTC epoch seconds at which the message has been +// accepted and stored in the entity. +func MessagingServiceBusMessageEnqueuedTime(val int) attribute.KeyValue { + return MessagingServiceBusMessageEnqueuedTimeKey.Int(val) +} + +// Enum values for messaging.operation.type +var ( + // A message is created. "Create" spans always refer to a single message and are + // used to provide a unique creation context for messages in batch sending + // scenarios. + // + // Stability: development + MessagingOperationTypeCreate = MessagingOperationTypeKey.String("create") + // One or more messages are provided for sending to an intermediary. If a single + // message is sent, the context of the "Send" span can be used as the creation + // context and no "Create" span needs to be created. + // + // Stability: development + MessagingOperationTypeSend = MessagingOperationTypeKey.String("send") + // One or more messages are requested by a consumer. This operation refers to + // pull-based scenarios, where consumers explicitly call methods of messaging + // SDKs to receive messages. + // + // Stability: development + MessagingOperationTypeReceive = MessagingOperationTypeKey.String("receive") + // One or more messages are processed by a consumer. + // + // Stability: development + MessagingOperationTypeProcess = MessagingOperationTypeKey.String("process") + // One or more messages are settled. + // + // Stability: development + MessagingOperationTypeSettle = MessagingOperationTypeKey.String("settle") +) + +// Enum values for messaging.rocketmq.consumption_model +var ( + // Clustering consumption model + // Stability: development + MessagingRocketMQConsumptionModelClustering = MessagingRocketMQConsumptionModelKey.String("clustering") + // Broadcasting consumption model + // Stability: development + MessagingRocketMQConsumptionModelBroadcasting = MessagingRocketMQConsumptionModelKey.String("broadcasting") +) + +// Enum values for messaging.rocketmq.message.type +var ( + // Normal message + // Stability: development + MessagingRocketMQMessageTypeNormal = MessagingRocketMQMessageTypeKey.String("normal") + // FIFO message + // Stability: development + MessagingRocketMQMessageTypeFifo = MessagingRocketMQMessageTypeKey.String("fifo") + // Delay message + // Stability: development + MessagingRocketMQMessageTypeDelay = MessagingRocketMQMessageTypeKey.String("delay") + // Transaction message + // Stability: development + MessagingRocketMQMessageTypeTransaction = MessagingRocketMQMessageTypeKey.String("transaction") +) + +// Enum values for messaging.servicebus.disposition_status +var ( + // Message is completed + // Stability: development + MessagingServiceBusDispositionStatusComplete = MessagingServiceBusDispositionStatusKey.String("complete") + // Message is abandoned + // Stability: development + MessagingServiceBusDispositionStatusAbandon = MessagingServiceBusDispositionStatusKey.String("abandon") + // Message is sent to dead letter queue + // Stability: development + MessagingServiceBusDispositionStatusDeadLetter = MessagingServiceBusDispositionStatusKey.String("dead_letter") + // Message is deferred + // Stability: development + MessagingServiceBusDispositionStatusDefer = MessagingServiceBusDispositionStatusKey.String("defer") +) + +// Enum values for messaging.system +var ( + // Apache ActiveMQ + // Stability: development + MessagingSystemActiveMQ = MessagingSystemKey.String("activemq") + // Amazon Simple Notification Service (SNS) + // Stability: development + MessagingSystemAWSSNS = MessagingSystemKey.String("aws.sns") + // Amazon Simple Queue Service (SQS) + // Stability: development + MessagingSystemAWSSQS = MessagingSystemKey.String("aws_sqs") + // Azure Event Grid + // Stability: development + MessagingSystemEventGrid = MessagingSystemKey.String("eventgrid") + // Azure Event Hubs + // Stability: development + MessagingSystemEventHubs = MessagingSystemKey.String("eventhubs") + // Azure Service Bus + // Stability: development + MessagingSystemServiceBus = MessagingSystemKey.String("servicebus") + // Google Cloud Pub/Sub + // Stability: development + MessagingSystemGCPPubSub = MessagingSystemKey.String("gcp_pubsub") + // Java Message Service + // Stability: development + MessagingSystemJMS = MessagingSystemKey.String("jms") + // Apache Kafka + // Stability: development + MessagingSystemKafka = MessagingSystemKey.String("kafka") + // RabbitMQ + // Stability: development + MessagingSystemRabbitMQ = MessagingSystemKey.String("rabbitmq") + // Apache RocketMQ + // Stability: development + MessagingSystemRocketMQ = MessagingSystemKey.String("rocketmq") + // Apache Pulsar + // Stability: development + MessagingSystemPulsar = MessagingSystemKey.String("pulsar") +) + +// Namespace: network +const ( + // NetworkCarrierICCKey is the attribute Key conforming to the + // "network.carrier.icc" semantic conventions. It represents the ISO 3166-1 + // alpha-2 2-character country code associated with the mobile carrier network. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: DE + NetworkCarrierICCKey = attribute.Key("network.carrier.icc") + + // NetworkCarrierMCCKey is the attribute Key conforming to the + // "network.carrier.mcc" semantic conventions. It represents the mobile carrier + // country code. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 310 + NetworkCarrierMCCKey = attribute.Key("network.carrier.mcc") + + // NetworkCarrierMNCKey is the attribute Key conforming to the + // "network.carrier.mnc" semantic conventions. It represents the mobile carrier + // network code. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 001 + NetworkCarrierMNCKey = attribute.Key("network.carrier.mnc") + + // NetworkCarrierNameKey is the attribute Key conforming to the + // "network.carrier.name" semantic conventions. It represents the name of the + // mobile carrier. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: sprint + NetworkCarrierNameKey = attribute.Key("network.carrier.name") + + // NetworkConnectionStateKey is the attribute Key conforming to the + // "network.connection.state" semantic conventions. It represents the state of + // network connection. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "close_wait" + // Note: Connection states are defined as part of the [rfc9293] + // + // [rfc9293]: https://datatracker.ietf.org/doc/html/rfc9293#section-3.3.2 + NetworkConnectionStateKey = attribute.Key("network.connection.state") + + // NetworkConnectionSubtypeKey is the attribute Key conforming to the + // "network.connection.subtype" semantic conventions. It represents the this + // describes more details regarding the connection.type. It may be the type of + // cell technology connection, but it could be used for describing details about + // a wifi connection. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: LTE + NetworkConnectionSubtypeKey = attribute.Key("network.connection.subtype") + + // NetworkConnectionTypeKey is the attribute Key conforming to the + // "network.connection.type" semantic conventions. It represents the internet + // connection type. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: wifi + NetworkConnectionTypeKey = attribute.Key("network.connection.type") + + // NetworkInterfaceNameKey is the attribute Key conforming to the + // "network.interface.name" semantic conventions. It represents the network + // interface name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "lo", "eth0" + NetworkInterfaceNameKey = attribute.Key("network.interface.name") + + // NetworkIODirectionKey is the attribute Key conforming to the + // "network.io.direction" semantic conventions. It represents the network IO + // operation direction. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "transmit" + NetworkIODirectionKey = attribute.Key("network.io.direction") + + // NetworkLocalAddressKey is the attribute Key conforming to the + // "network.local.address" semantic conventions. It represents the local address + // of the network connection - IP address or Unix domain socket name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "10.1.2.80", "/tmp/my.sock" + NetworkLocalAddressKey = attribute.Key("network.local.address") + + // NetworkLocalPortKey is the attribute Key conforming to the + // "network.local.port" semantic conventions. It represents the local port + // number of the network connection. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: 65123 + NetworkLocalPortKey = attribute.Key("network.local.port") + + // NetworkPeerAddressKey is the attribute Key conforming to the + // "network.peer.address" semantic conventions. It represents the peer address + // of the network connection - IP address or Unix domain socket name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "10.1.2.80", "/tmp/my.sock" + NetworkPeerAddressKey = attribute.Key("network.peer.address") + + // NetworkPeerPortKey is the attribute Key conforming to the "network.peer.port" + // semantic conventions. It represents the peer port number of the network + // connection. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: 65123 + NetworkPeerPortKey = attribute.Key("network.peer.port") + + // NetworkProtocolNameKey is the attribute Key conforming to the + // "network.protocol.name" semantic conventions. It represents the + // [OSI application layer] or non-OSI equivalent. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "amqp", "http", "mqtt" + // Note: The value SHOULD be normalized to lowercase. + // + // [OSI application layer]: https://wikipedia.org/wiki/Application_layer + NetworkProtocolNameKey = attribute.Key("network.protocol.name") + + // NetworkProtocolVersionKey is the attribute Key conforming to the + // "network.protocol.version" semantic conventions. It represents the actual + // version of the protocol used for network communication. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "1.1", "2" + // Note: If protocol version is subject to negotiation (for example using [ALPN] + // ), this attribute SHOULD be set to the negotiated version. If the actual + // protocol version is not known, this attribute SHOULD NOT be set. + // + // [ALPN]: https://www.rfc-editor.org/rfc/rfc7301.html + NetworkProtocolVersionKey = attribute.Key("network.protocol.version") + + // NetworkTransportKey is the attribute Key conforming to the + // "network.transport" semantic conventions. It represents the + // [OSI transport layer] or [inter-process communication method]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "tcp", "udp" + // Note: The value SHOULD be normalized to lowercase. + // + // Consider always setting the transport when setting a port number, since + // a port number is ambiguous without knowing the transport. For example + // different processes could be listening on TCP port 12345 and UDP port 12345. + // + // [OSI transport layer]: https://wikipedia.org/wiki/Transport_layer + // [inter-process communication method]: https://wikipedia.org/wiki/Inter-process_communication + NetworkTransportKey = attribute.Key("network.transport") + + // NetworkTypeKey is the attribute Key conforming to the "network.type" semantic + // conventions. It represents the [OSI network layer] or non-OSI equivalent. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "ipv4", "ipv6" + // Note: The value SHOULD be normalized to lowercase. + // + // [OSI network layer]: https://wikipedia.org/wiki/Network_layer + NetworkTypeKey = attribute.Key("network.type") +) + +// NetworkCarrierICC returns an attribute KeyValue conforming to the +// "network.carrier.icc" semantic conventions. It represents the ISO 3166-1 +// alpha-2 2-character country code associated with the mobile carrier network. +func NetworkCarrierICC(val string) attribute.KeyValue { + return NetworkCarrierICCKey.String(val) +} + +// NetworkCarrierMCC returns an attribute KeyValue conforming to the +// "network.carrier.mcc" semantic conventions. It represents the mobile carrier +// country code. +func NetworkCarrierMCC(val string) attribute.KeyValue { + return NetworkCarrierMCCKey.String(val) +} + +// NetworkCarrierMNC returns an attribute KeyValue conforming to the +// "network.carrier.mnc" semantic conventions. It represents the mobile carrier +// network code. +func NetworkCarrierMNC(val string) attribute.KeyValue { + return NetworkCarrierMNCKey.String(val) +} + +// NetworkCarrierName returns an attribute KeyValue conforming to the +// "network.carrier.name" semantic conventions. It represents the name of the +// mobile carrier. +func NetworkCarrierName(val string) attribute.KeyValue { + return NetworkCarrierNameKey.String(val) +} + +// NetworkInterfaceName returns an attribute KeyValue conforming to the +// "network.interface.name" semantic conventions. It represents the network +// interface name. +func NetworkInterfaceName(val string) attribute.KeyValue { + return NetworkInterfaceNameKey.String(val) +} + +// NetworkLocalAddress returns an attribute KeyValue conforming to the +// "network.local.address" semantic conventions. It represents the local address +// of the network connection - IP address or Unix domain socket name. +func NetworkLocalAddress(val string) attribute.KeyValue { + return NetworkLocalAddressKey.String(val) +} + +// NetworkLocalPort returns an attribute KeyValue conforming to the +// "network.local.port" semantic conventions. It represents the local port number +// of the network connection. +func NetworkLocalPort(val int) attribute.KeyValue { + return NetworkLocalPortKey.Int(val) +} + +// NetworkPeerAddress returns an attribute KeyValue conforming to the +// "network.peer.address" semantic conventions. It represents the peer address of +// the network connection - IP address or Unix domain socket name. +func NetworkPeerAddress(val string) attribute.KeyValue { + return NetworkPeerAddressKey.String(val) +} + +// NetworkPeerPort returns an attribute KeyValue conforming to the +// "network.peer.port" semantic conventions. It represents the peer port number +// of the network connection. +func NetworkPeerPort(val int) attribute.KeyValue { + return NetworkPeerPortKey.Int(val) +} + +// NetworkProtocolName returns an attribute KeyValue conforming to the +// "network.protocol.name" semantic conventions. It represents the +// [OSI application layer] or non-OSI equivalent. +// +// [OSI application layer]: https://wikipedia.org/wiki/Application_layer +func NetworkProtocolName(val string) attribute.KeyValue { + return NetworkProtocolNameKey.String(val) +} + +// NetworkProtocolVersion returns an attribute KeyValue conforming to the +// "network.protocol.version" semantic conventions. It represents the actual +// version of the protocol used for network communication. +func NetworkProtocolVersion(val string) attribute.KeyValue { + return NetworkProtocolVersionKey.String(val) +} + +// Enum values for network.connection.state +var ( + // closed + // Stability: development + NetworkConnectionStateClosed = NetworkConnectionStateKey.String("closed") + // close_wait + // Stability: development + NetworkConnectionStateCloseWait = NetworkConnectionStateKey.String("close_wait") + // closing + // Stability: development + NetworkConnectionStateClosing = NetworkConnectionStateKey.String("closing") + // established + // Stability: development + NetworkConnectionStateEstablished = NetworkConnectionStateKey.String("established") + // fin_wait_1 + // Stability: development + NetworkConnectionStateFinWait1 = NetworkConnectionStateKey.String("fin_wait_1") + // fin_wait_2 + // Stability: development + NetworkConnectionStateFinWait2 = NetworkConnectionStateKey.String("fin_wait_2") + // last_ack + // Stability: development + NetworkConnectionStateLastAck = NetworkConnectionStateKey.String("last_ack") + // listen + // Stability: development + NetworkConnectionStateListen = NetworkConnectionStateKey.String("listen") + // syn_received + // Stability: development + NetworkConnectionStateSynReceived = NetworkConnectionStateKey.String("syn_received") + // syn_sent + // Stability: development + NetworkConnectionStateSynSent = NetworkConnectionStateKey.String("syn_sent") + // time_wait + // Stability: development + NetworkConnectionStateTimeWait = NetworkConnectionStateKey.String("time_wait") +) + +// Enum values for network.connection.subtype +var ( + // GPRS + // Stability: development + NetworkConnectionSubtypeGprs = NetworkConnectionSubtypeKey.String("gprs") + // EDGE + // Stability: development + NetworkConnectionSubtypeEdge = NetworkConnectionSubtypeKey.String("edge") + // UMTS + // Stability: development + NetworkConnectionSubtypeUmts = NetworkConnectionSubtypeKey.String("umts") + // CDMA + // Stability: development + NetworkConnectionSubtypeCdma = NetworkConnectionSubtypeKey.String("cdma") + // EVDO Rel. 0 + // Stability: development + NetworkConnectionSubtypeEvdo0 = NetworkConnectionSubtypeKey.String("evdo_0") + // EVDO Rev. A + // Stability: development + NetworkConnectionSubtypeEvdoA = NetworkConnectionSubtypeKey.String("evdo_a") + // CDMA2000 1XRTT + // Stability: development + NetworkConnectionSubtypeCdma20001xrtt = NetworkConnectionSubtypeKey.String("cdma2000_1xrtt") + // HSDPA + // Stability: development + NetworkConnectionSubtypeHsdpa = NetworkConnectionSubtypeKey.String("hsdpa") + // HSUPA + // Stability: development + NetworkConnectionSubtypeHsupa = NetworkConnectionSubtypeKey.String("hsupa") + // HSPA + // Stability: development + NetworkConnectionSubtypeHspa = NetworkConnectionSubtypeKey.String("hspa") + // IDEN + // Stability: development + NetworkConnectionSubtypeIden = NetworkConnectionSubtypeKey.String("iden") + // EVDO Rev. B + // Stability: development + NetworkConnectionSubtypeEvdoB = NetworkConnectionSubtypeKey.String("evdo_b") + // LTE + // Stability: development + NetworkConnectionSubtypeLte = NetworkConnectionSubtypeKey.String("lte") + // EHRPD + // Stability: development + NetworkConnectionSubtypeEhrpd = NetworkConnectionSubtypeKey.String("ehrpd") + // HSPAP + // Stability: development + NetworkConnectionSubtypeHspap = NetworkConnectionSubtypeKey.String("hspap") + // GSM + // Stability: development + NetworkConnectionSubtypeGsm = NetworkConnectionSubtypeKey.String("gsm") + // TD-SCDMA + // Stability: development + NetworkConnectionSubtypeTdScdma = NetworkConnectionSubtypeKey.String("td_scdma") + // IWLAN + // Stability: development + NetworkConnectionSubtypeIwlan = NetworkConnectionSubtypeKey.String("iwlan") + // 5G NR (New Radio) + // Stability: development + NetworkConnectionSubtypeNr = NetworkConnectionSubtypeKey.String("nr") + // 5G NRNSA (New Radio Non-Standalone) + // Stability: development + NetworkConnectionSubtypeNrnsa = NetworkConnectionSubtypeKey.String("nrnsa") + // LTE CA + // Stability: development + NetworkConnectionSubtypeLteCa = NetworkConnectionSubtypeKey.String("lte_ca") +) + +// Enum values for network.connection.type +var ( + // wifi + // Stability: development + NetworkConnectionTypeWifi = NetworkConnectionTypeKey.String("wifi") + // wired + // Stability: development + NetworkConnectionTypeWired = NetworkConnectionTypeKey.String("wired") + // cell + // Stability: development + NetworkConnectionTypeCell = NetworkConnectionTypeKey.String("cell") + // unavailable + // Stability: development + NetworkConnectionTypeUnavailable = NetworkConnectionTypeKey.String("unavailable") + // unknown + // Stability: development + NetworkConnectionTypeUnknown = NetworkConnectionTypeKey.String("unknown") +) + +// Enum values for network.io.direction +var ( + // transmit + // Stability: development + NetworkIODirectionTransmit = NetworkIODirectionKey.String("transmit") + // receive + // Stability: development + NetworkIODirectionReceive = NetworkIODirectionKey.String("receive") +) + +// Enum values for network.transport +var ( + // TCP + // Stability: stable + NetworkTransportTCP = NetworkTransportKey.String("tcp") + // UDP + // Stability: stable + NetworkTransportUDP = NetworkTransportKey.String("udp") + // Named or anonymous pipe. + // Stability: stable + NetworkTransportPipe = NetworkTransportKey.String("pipe") + // Unix domain socket + // Stability: stable + NetworkTransportUnix = NetworkTransportKey.String("unix") + // QUIC + // Stability: stable + NetworkTransportQUIC = NetworkTransportKey.String("quic") +) + +// Enum values for network.type +var ( + // IPv4 + // Stability: stable + NetworkTypeIPv4 = NetworkTypeKey.String("ipv4") + // IPv6 + // Stability: stable + NetworkTypeIPv6 = NetworkTypeKey.String("ipv6") +) + +// Namespace: nfs +const ( + // NfsOperationNameKey is the attribute Key conforming to the + // "nfs.operation.name" semantic conventions. It represents the NFSv4+ operation + // name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "OPEN", "READ", "GETATTR" + NfsOperationNameKey = attribute.Key("nfs.operation.name") + + // NfsServerRepcacheStatusKey is the attribute Key conforming to the + // "nfs.server.repcache.status" semantic conventions. It represents the linux: + // one of "hit" (NFSD_STATS_RC_HITS), "miss" (NFSD_STATS_RC_MISSES), or + // "nocache" (NFSD_STATS_RC_NOCACHE -- uncacheable). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: hit + NfsServerRepcacheStatusKey = attribute.Key("nfs.server.repcache.status") +) + +// NfsOperationName returns an attribute KeyValue conforming to the +// "nfs.operation.name" semantic conventions. It represents the NFSv4+ operation +// name. +func NfsOperationName(val string) attribute.KeyValue { + return NfsOperationNameKey.String(val) +} + +// NfsServerRepcacheStatus returns an attribute KeyValue conforming to the +// "nfs.server.repcache.status" semantic conventions. It represents the linux: +// one of "hit" (NFSD_STATS_RC_HITS), "miss" (NFSD_STATS_RC_MISSES), or "nocache" +// (NFSD_STATS_RC_NOCACHE -- uncacheable). +func NfsServerRepcacheStatus(val string) attribute.KeyValue { + return NfsServerRepcacheStatusKey.String(val) +} + +// Namespace: oci +const ( + // OCIManifestDigestKey is the attribute Key conforming to the + // "oci.manifest.digest" semantic conventions. It represents the digest of the + // OCI image manifest. For container images specifically is the digest by which + // the container image is known. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "sha256:e4ca62c0d62f3e886e684806dfe9d4e0cda60d54986898173c1083856cfda0f4" + // Note: Follows [OCI Image Manifest Specification], and specifically the + // [Digest property]. + // An example can be found in [Example Image Manifest]. + // + // [OCI Image Manifest Specification]: https://github.com/opencontainers/image-spec/blob/main/manifest.md + // [Digest property]: https://github.com/opencontainers/image-spec/blob/main/descriptor.md#digests + // [Example Image Manifest]: https://github.com/opencontainers/image-spec/blob/main/manifest.md#example-image-manifest + OCIManifestDigestKey = attribute.Key("oci.manifest.digest") +) + +// OCIManifestDigest returns an attribute KeyValue conforming to the +// "oci.manifest.digest" semantic conventions. It represents the digest of the +// OCI image manifest. For container images specifically is the digest by which +// the container image is known. +func OCIManifestDigest(val string) attribute.KeyValue { + return OCIManifestDigestKey.String(val) +} + +// Namespace: onc_rpc +const ( + // OncRPCProcedureNameKey is the attribute Key conforming to the + // "onc_rpc.procedure.name" semantic conventions. It represents the ONC/Sun RPC + // procedure name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "OPEN", "READ", "GETATTR" + OncRPCProcedureNameKey = attribute.Key("onc_rpc.procedure.name") + + // OncRPCProcedureNumberKey is the attribute Key conforming to the + // "onc_rpc.procedure.number" semantic conventions. It represents the ONC/Sun + // RPC procedure number. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + OncRPCProcedureNumberKey = attribute.Key("onc_rpc.procedure.number") + + // OncRPCProgramNameKey is the attribute Key conforming to the + // "onc_rpc.program.name" semantic conventions. It represents the ONC/Sun RPC + // program name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "portmapper", "nfs" + OncRPCProgramNameKey = attribute.Key("onc_rpc.program.name") + + // OncRPCVersionKey is the attribute Key conforming to the "onc_rpc.version" + // semantic conventions. It represents the ONC/Sun RPC program version. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + OncRPCVersionKey = attribute.Key("onc_rpc.version") +) + +// OncRPCProcedureName returns an attribute KeyValue conforming to the +// "onc_rpc.procedure.name" semantic conventions. It represents the ONC/Sun RPC +// procedure name. +func OncRPCProcedureName(val string) attribute.KeyValue { + return OncRPCProcedureNameKey.String(val) +} + +// OncRPCProcedureNumber returns an attribute KeyValue conforming to the +// "onc_rpc.procedure.number" semantic conventions. It represents the ONC/Sun RPC +// procedure number. +func OncRPCProcedureNumber(val int) attribute.KeyValue { + return OncRPCProcedureNumberKey.Int(val) +} + +// OncRPCProgramName returns an attribute KeyValue conforming to the +// "onc_rpc.program.name" semantic conventions. It represents the ONC/Sun RPC +// program name. +func OncRPCProgramName(val string) attribute.KeyValue { + return OncRPCProgramNameKey.String(val) +} + +// OncRPCVersion returns an attribute KeyValue conforming to the +// "onc_rpc.version" semantic conventions. It represents the ONC/Sun RPC program +// version. +func OncRPCVersion(val int) attribute.KeyValue { + return OncRPCVersionKey.Int(val) +} + +// Namespace: openai +const ( + // OpenAIRequestServiceTierKey is the attribute Key conforming to the + // "openai.request.service_tier" semantic conventions. It represents the service + // tier requested. May be a specific tier, default, or auto. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "auto", "default" + OpenAIRequestServiceTierKey = attribute.Key("openai.request.service_tier") + + // OpenAIResponseServiceTierKey is the attribute Key conforming to the + // "openai.response.service_tier" semantic conventions. It represents the + // service tier used for the response. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "scale", "default" + OpenAIResponseServiceTierKey = attribute.Key("openai.response.service_tier") + + // OpenAIResponseSystemFingerprintKey is the attribute Key conforming to the + // "openai.response.system_fingerprint" semantic conventions. It represents a + // fingerprint to track any eventual change in the Generative AI environment. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "fp_44709d6fcb" + OpenAIResponseSystemFingerprintKey = attribute.Key("openai.response.system_fingerprint") +) + +// OpenAIResponseServiceTier returns an attribute KeyValue conforming to the +// "openai.response.service_tier" semantic conventions. It represents the service +// tier used for the response. +func OpenAIResponseServiceTier(val string) attribute.KeyValue { + return OpenAIResponseServiceTierKey.String(val) +} + +// OpenAIResponseSystemFingerprint returns an attribute KeyValue conforming to +// the "openai.response.system_fingerprint" semantic conventions. It represents a +// fingerprint to track any eventual change in the Generative AI environment. +func OpenAIResponseSystemFingerprint(val string) attribute.KeyValue { + return OpenAIResponseSystemFingerprintKey.String(val) +} + +// Enum values for openai.request.service_tier +var ( + // The system will utilize scale tier credits until they are exhausted. + // Stability: development + OpenAIRequestServiceTierAuto = OpenAIRequestServiceTierKey.String("auto") + // The system will utilize the default scale tier. + // Stability: development + OpenAIRequestServiceTierDefault = OpenAIRequestServiceTierKey.String("default") +) + +// Namespace: openshift +const ( + // OpenShiftClusterquotaNameKey is the attribute Key conforming to the + // "openshift.clusterquota.name" semantic conventions. It represents the name of + // the cluster quota. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "opentelemetry" + OpenShiftClusterquotaNameKey = attribute.Key("openshift.clusterquota.name") + + // OpenShiftClusterquotaUIDKey is the attribute Key conforming to the + // "openshift.clusterquota.uid" semantic conventions. It represents the UID of + // the cluster quota. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + OpenShiftClusterquotaUIDKey = attribute.Key("openshift.clusterquota.uid") +) + +// OpenShiftClusterquotaName returns an attribute KeyValue conforming to the +// "openshift.clusterquota.name" semantic conventions. It represents the name of +// the cluster quota. +func OpenShiftClusterquotaName(val string) attribute.KeyValue { + return OpenShiftClusterquotaNameKey.String(val) +} + +// OpenShiftClusterquotaUID returns an attribute KeyValue conforming to the +// "openshift.clusterquota.uid" semantic conventions. It represents the UID of +// the cluster quota. +func OpenShiftClusterquotaUID(val string) attribute.KeyValue { + return OpenShiftClusterquotaUIDKey.String(val) +} + +// Namespace: opentracing +const ( + // OpenTracingRefTypeKey is the attribute Key conforming to the + // "opentracing.ref_type" semantic conventions. It represents the parent-child + // Reference type. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: The causal relationship between a child Span and a parent Span. + OpenTracingRefTypeKey = attribute.Key("opentracing.ref_type") +) + +// Enum values for opentracing.ref_type +var ( + // The parent Span depends on the child Span in some capacity + // Stability: development + OpenTracingRefTypeChildOf = OpenTracingRefTypeKey.String("child_of") + // The parent Span doesn't depend in any way on the result of the child Span + // Stability: development + OpenTracingRefTypeFollowsFrom = OpenTracingRefTypeKey.String("follows_from") +) + +// Namespace: os +const ( + // OSBuildIDKey is the attribute Key conforming to the "os.build_id" semantic + // conventions. It represents the unique identifier for a particular build or + // compilation of the operating system. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "TQ3C.230805.001.B2", "20E247", "22621" + OSBuildIDKey = attribute.Key("os.build_id") + + // OSDescriptionKey is the attribute Key conforming to the "os.description" + // semantic conventions. It represents the human readable (not intended to be + // parsed) OS version information, like e.g. reported by `ver` or + // `lsb_release -a` commands. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Microsoft Windows [Version 10.0.18363.778]", "Ubuntu 18.04.1 LTS" + OSDescriptionKey = attribute.Key("os.description") + + // OSNameKey is the attribute Key conforming to the "os.name" semantic + // conventions. It represents the human readable operating system name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "iOS", "Android", "Ubuntu" + OSNameKey = attribute.Key("os.name") + + // OSTypeKey is the attribute Key conforming to the "os.type" semantic + // conventions. It represents the operating system type. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + OSTypeKey = attribute.Key("os.type") + + // OSVersionKey is the attribute Key conforming to the "os.version" semantic + // conventions. It represents the version string of the operating system as + // defined in [Version Attributes]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "14.2.1", "18.04.1" + // + // [Version Attributes]: /docs/resource/README.md#version-attributes + OSVersionKey = attribute.Key("os.version") +) + +// OSBuildID returns an attribute KeyValue conforming to the "os.build_id" +// semantic conventions. It represents the unique identifier for a particular +// build or compilation of the operating system. +func OSBuildID(val string) attribute.KeyValue { + return OSBuildIDKey.String(val) +} + +// OSDescription returns an attribute KeyValue conforming to the "os.description" +// semantic conventions. It represents the human readable (not intended to be +// parsed) OS version information, like e.g. reported by `ver` or +// `lsb_release -a` commands. +func OSDescription(val string) attribute.KeyValue { + return OSDescriptionKey.String(val) +} + +// OSName returns an attribute KeyValue conforming to the "os.name" semantic +// conventions. It represents the human readable operating system name. +func OSName(val string) attribute.KeyValue { + return OSNameKey.String(val) +} + +// OSVersion returns an attribute KeyValue conforming to the "os.version" +// semantic conventions. It represents the version string of the operating system +// as defined in [Version Attributes]. +// +// [Version Attributes]: /docs/resource/README.md#version-attributes +func OSVersion(val string) attribute.KeyValue { + return OSVersionKey.String(val) +} + +// Enum values for os.type +var ( + // Microsoft Windows + // Stability: development + OSTypeWindows = OSTypeKey.String("windows") + // Linux + // Stability: development + OSTypeLinux = OSTypeKey.String("linux") + // Apple Darwin + // Stability: development + OSTypeDarwin = OSTypeKey.String("darwin") + // FreeBSD + // Stability: development + OSTypeFreeBSD = OSTypeKey.String("freebsd") + // NetBSD + // Stability: development + OSTypeNetBSD = OSTypeKey.String("netbsd") + // OpenBSD + // Stability: development + OSTypeOpenBSD = OSTypeKey.String("openbsd") + // DragonFly BSD + // Stability: development + OSTypeDragonflyBSD = OSTypeKey.String("dragonflybsd") + // HP-UX (Hewlett Packard Unix) + // Stability: development + OSTypeHPUX = OSTypeKey.String("hpux") + // AIX (Advanced Interactive eXecutive) + // Stability: development + OSTypeAIX = OSTypeKey.String("aix") + // SunOS, Oracle Solaris + // Stability: development + OSTypeSolaris = OSTypeKey.String("solaris") + // IBM z/OS + // Stability: development + OSTypeZOS = OSTypeKey.String("zos") +) + +// Namespace: otel +const ( + // OTelComponentNameKey is the attribute Key conforming to the + // "otel.component.name" semantic conventions. It represents a name uniquely + // identifying the instance of the OpenTelemetry component within its containing + // SDK instance. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "otlp_grpc_span_exporter/0", "custom-name" + // Note: Implementations SHOULD ensure a low cardinality for this attribute, + // even across application or SDK restarts. + // E.g. implementations MUST NOT use UUIDs as values for this attribute. + // + // Implementations MAY achieve these goals by following a + // `/` pattern, e.g. + // `batching_span_processor/0`. + // Hereby `otel.component.type` refers to the corresponding attribute value of + // the component. + // + // The value of `instance-counter` MAY be automatically assigned by the + // component and uniqueness within the enclosing SDK instance MUST be + // guaranteed. + // For example, `` MAY be implemented by using a monotonically + // increasing counter (starting with `0`), which is incremented every time an + // instance of the given component type is started. + // + // With this implementation, for example the first Batching Span Processor would + // have `batching_span_processor/0` + // as `otel.component.name`, the second one `batching_span_processor/1` and so + // on. + // These values will therefore be reused in the case of an application restart. + OTelComponentNameKey = attribute.Key("otel.component.name") + + // OTelComponentTypeKey is the attribute Key conforming to the + // "otel.component.type" semantic conventions. It represents a name identifying + // the type of the OpenTelemetry component. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "batching_span_processor", "com.example.MySpanExporter" + // Note: If none of the standardized values apply, implementations SHOULD use + // the language-defined name of the type. + // E.g. for Java the fully qualified classname SHOULD be used in this case. + OTelComponentTypeKey = attribute.Key("otel.component.type") + + // OTelEventNameKey is the attribute Key conforming to the "otel.event.name" + // semantic conventions. It represents the identifies the class / type of event. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "browser.mouse.click", "device.app.lifecycle" + // Note: This attribute SHOULD be used by non-OTLP exporters when destination + // does not support `EventName` or equivalent field. This attribute MAY be used + // by applications using existing logging libraries so that it can be used to + // set the `EventName` field by Collector or SDK components. + OTelEventNameKey = attribute.Key("otel.event.name") + + // OTelScopeNameKey is the attribute Key conforming to the "otel.scope.name" + // semantic conventions. It represents the name of the instrumentation scope - ( + // `InstrumentationScope.Name` in OTLP). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "io.opentelemetry.contrib.mongodb" + OTelScopeNameKey = attribute.Key("otel.scope.name") + + // OTelScopeSchemaURLKey is the attribute Key conforming to the + // "otel.scope.schema_url" semantic conventions. It represents the schema URL of + // the instrumentation scope. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "https://opentelemetry.io/schemas/1.31.0" + OTelScopeSchemaURLKey = attribute.Key("otel.scope.schema_url") + + // OTelScopeVersionKey is the attribute Key conforming to the + // "otel.scope.version" semantic conventions. It represents the version of the + // instrumentation scope - (`InstrumentationScope.Version` in OTLP). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "1.0.0" + OTelScopeVersionKey = attribute.Key("otel.scope.version") + + // OTelSpanParentOriginKey is the attribute Key conforming to the + // "otel.span.parent.origin" semantic conventions. It represents the determines + // whether the span has a parent span, and if so, + // [whether it is a remote parent]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // + // [whether it is a remote parent]: https://opentelemetry.io/docs/specs/otel/trace/api/#isremote + OTelSpanParentOriginKey = attribute.Key("otel.span.parent.origin") + + // OTelSpanSamplingResultKey is the attribute Key conforming to the + // "otel.span.sampling_result" semantic conventions. It represents the result + // value of the sampler for this span. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + OTelSpanSamplingResultKey = attribute.Key("otel.span.sampling_result") + + // OTelStatusCodeKey is the attribute Key conforming to the "otel.status_code" + // semantic conventions. It represents the name of the code, either "OK" or + // "ERROR". MUST NOT be set if the status code is UNSET. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: + OTelStatusCodeKey = attribute.Key("otel.status_code") + + // OTelStatusDescriptionKey is the attribute Key conforming to the + // "otel.status_description" semantic conventions. It represents the description + // of the Status if it has a value, otherwise not set. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "resource not found" + OTelStatusDescriptionKey = attribute.Key("otel.status_description") +) + +// OTelComponentName returns an attribute KeyValue conforming to the +// "otel.component.name" semantic conventions. It represents a name uniquely +// identifying the instance of the OpenTelemetry component within its containing +// SDK instance. +func OTelComponentName(val string) attribute.KeyValue { + return OTelComponentNameKey.String(val) +} + +// OTelEventName returns an attribute KeyValue conforming to the +// "otel.event.name" semantic conventions. It represents the identifies the class +// / type of event. +func OTelEventName(val string) attribute.KeyValue { + return OTelEventNameKey.String(val) +} + +// OTelScopeName returns an attribute KeyValue conforming to the +// "otel.scope.name" semantic conventions. It represents the name of the +// instrumentation scope - (`InstrumentationScope.Name` in OTLP). +func OTelScopeName(val string) attribute.KeyValue { + return OTelScopeNameKey.String(val) +} + +// OTelScopeSchemaURL returns an attribute KeyValue conforming to the +// "otel.scope.schema_url" semantic conventions. It represents the schema URL of +// the instrumentation scope. +func OTelScopeSchemaURL(val string) attribute.KeyValue { + return OTelScopeSchemaURLKey.String(val) +} + +// OTelScopeVersion returns an attribute KeyValue conforming to the +// "otel.scope.version" semantic conventions. It represents the version of the +// instrumentation scope - (`InstrumentationScope.Version` in OTLP). +func OTelScopeVersion(val string) attribute.KeyValue { + return OTelScopeVersionKey.String(val) +} + +// OTelStatusDescription returns an attribute KeyValue conforming to the +// "otel.status_description" semantic conventions. It represents the description +// of the Status if it has a value, otherwise not set. +func OTelStatusDescription(val string) attribute.KeyValue { + return OTelStatusDescriptionKey.String(val) +} + +// Enum values for otel.component.type +var ( + // The builtin SDK batching span processor + // + // Stability: development + OTelComponentTypeBatchingSpanProcessor = OTelComponentTypeKey.String("batching_span_processor") + // The builtin SDK simple span processor + // + // Stability: development + OTelComponentTypeSimpleSpanProcessor = OTelComponentTypeKey.String("simple_span_processor") + // The builtin SDK batching log record processor + // + // Stability: development + OTelComponentTypeBatchingLogProcessor = OTelComponentTypeKey.String("batching_log_processor") + // The builtin SDK simple log record processor + // + // Stability: development + OTelComponentTypeSimpleLogProcessor = OTelComponentTypeKey.String("simple_log_processor") + // OTLP span exporter over gRPC with protobuf serialization + // + // Stability: development + OTelComponentTypeOtlpGRPCSpanExporter = OTelComponentTypeKey.String("otlp_grpc_span_exporter") + // OTLP span exporter over HTTP with protobuf serialization + // + // Stability: development + OTelComponentTypeOtlpHTTPSpanExporter = OTelComponentTypeKey.String("otlp_http_span_exporter") + // OTLP span exporter over HTTP with JSON serialization + // + // Stability: development + OTelComponentTypeOtlpHTTPJSONSpanExporter = OTelComponentTypeKey.String("otlp_http_json_span_exporter") + // Zipkin span exporter over HTTP + // + // Stability: development + OTelComponentTypeZipkinHTTPSpanExporter = OTelComponentTypeKey.String("zipkin_http_span_exporter") + // OTLP log record exporter over gRPC with protobuf serialization + // + // Stability: development + OTelComponentTypeOtlpGRPCLogExporter = OTelComponentTypeKey.String("otlp_grpc_log_exporter") + // OTLP log record exporter over HTTP with protobuf serialization + // + // Stability: development + OTelComponentTypeOtlpHTTPLogExporter = OTelComponentTypeKey.String("otlp_http_log_exporter") + // OTLP log record exporter over HTTP with JSON serialization + // + // Stability: development + OTelComponentTypeOtlpHTTPJSONLogExporter = OTelComponentTypeKey.String("otlp_http_json_log_exporter") + // The builtin SDK periodically exporting metric reader + // + // Stability: development + OTelComponentTypePeriodicMetricReader = OTelComponentTypeKey.String("periodic_metric_reader") + // OTLP metric exporter over gRPC with protobuf serialization + // + // Stability: development + OTelComponentTypeOtlpGRPCMetricExporter = OTelComponentTypeKey.String("otlp_grpc_metric_exporter") + // OTLP metric exporter over HTTP with protobuf serialization + // + // Stability: development + OTelComponentTypeOtlpHTTPMetricExporter = OTelComponentTypeKey.String("otlp_http_metric_exporter") + // OTLP metric exporter over HTTP with JSON serialization + // + // Stability: development + OTelComponentTypeOtlpHTTPJSONMetricExporter = OTelComponentTypeKey.String("otlp_http_json_metric_exporter") + // Prometheus metric exporter over HTTP with the default text-based format + // + // Stability: development + OTelComponentTypePrometheusHTTPTextMetricExporter = OTelComponentTypeKey.String("prometheus_http_text_metric_exporter") +) + +// Enum values for otel.span.parent.origin +var ( + // The span does not have a parent, it is a root span + // Stability: development + OTelSpanParentOriginNone = OTelSpanParentOriginKey.String("none") + // The span has a parent and the parent's span context [isRemote()] is false + // Stability: development + // + // [isRemote()]: https://opentelemetry.io/docs/specs/otel/trace/api/#isremote + OTelSpanParentOriginLocal = OTelSpanParentOriginKey.String("local") + // The span has a parent and the parent's span context [isRemote()] is true + // Stability: development + // + // [isRemote()]: https://opentelemetry.io/docs/specs/otel/trace/api/#isremote + OTelSpanParentOriginRemote = OTelSpanParentOriginKey.String("remote") +) + +// Enum values for otel.span.sampling_result +var ( + // The span is not sampled and not recording + // Stability: development + OTelSpanSamplingResultDrop = OTelSpanSamplingResultKey.String("DROP") + // The span is not sampled, but recording + // Stability: development + OTelSpanSamplingResultRecordOnly = OTelSpanSamplingResultKey.String("RECORD_ONLY") + // The span is sampled and recording + // Stability: development + OTelSpanSamplingResultRecordAndSample = OTelSpanSamplingResultKey.String("RECORD_AND_SAMPLE") +) + +// Enum values for otel.status_code +var ( + // The operation has been validated by an Application developer or Operator to + // have completed successfully. + // Stability: stable + OTelStatusCodeOk = OTelStatusCodeKey.String("OK") + // The operation contains an error. + // Stability: stable + OTelStatusCodeError = OTelStatusCodeKey.String("ERROR") +) + +// Namespace: pprof +const ( + // PprofLocationIsFoldedKey is the attribute Key conforming to the + // "pprof.location.is_folded" semantic conventions. It represents the provides + // an indication that multiple symbols map to this location's address, for + // example due to identical code folding by the linker. In that case the line + // information represents one of the multiple symbols. This field must be + // recomputed when the symbolization state of the profile changes. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + PprofLocationIsFoldedKey = attribute.Key("pprof.location.is_folded") + + // PprofMappingHasFilenamesKey is the attribute Key conforming to the + // "pprof.mapping.has_filenames" semantic conventions. It represents the + // indicates that there are filenames related to this mapping. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + PprofMappingHasFilenamesKey = attribute.Key("pprof.mapping.has_filenames") + + // PprofMappingHasFunctionsKey is the attribute Key conforming to the + // "pprof.mapping.has_functions" semantic conventions. It represents the + // indicates that there are functions related to this mapping. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + PprofMappingHasFunctionsKey = attribute.Key("pprof.mapping.has_functions") + + // PprofMappingHasInlineFramesKey is the attribute Key conforming to the + // "pprof.mapping.has_inline_frames" semantic conventions. It represents the + // indicates that there are inline frames related to this mapping. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + PprofMappingHasInlineFramesKey = attribute.Key("pprof.mapping.has_inline_frames") + + // PprofMappingHasLineNumbersKey is the attribute Key conforming to the + // "pprof.mapping.has_line_numbers" semantic conventions. It represents the + // indicates that there are line numbers related to this mapping. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + PprofMappingHasLineNumbersKey = attribute.Key("pprof.mapping.has_line_numbers") + + // PprofProfileCommentKey is the attribute Key conforming to the + // "pprof.profile.comment" semantic conventions. It represents the free-form + // text associated with the profile. This field should not be used to store any + // machine-readable information, it is only for human-friendly content. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "hello world", "bazinga" + PprofProfileCommentKey = attribute.Key("pprof.profile.comment") + + // PprofProfileDocURLKey is the attribute Key conforming to the + // "pprof.profile.doc_url" semantic conventions. It represents the documentation + // link for this profile type. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "http://pprof.example.com/cpu-profile.html" + // Note: The URL must be absolute and may be missing if the profile was + // generated by code that did not supply a link + PprofProfileDocURLKey = attribute.Key("pprof.profile.doc_url") + + // PprofProfileDropFramesKey is the attribute Key conforming to the + // "pprof.profile.drop_frames" semantic conventions. It represents the frames + // with Function.function_name fully matching the regexp will be dropped from + // the samples, along with their successors. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/foobar/" + PprofProfileDropFramesKey = attribute.Key("pprof.profile.drop_frames") + + // PprofProfileKeepFramesKey is the attribute Key conforming to the + // "pprof.profile.keep_frames" semantic conventions. It represents the frames + // with Function.function_name fully matching the regexp will be kept, even if + // it matches drop_frames. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/bazinga/" + PprofProfileKeepFramesKey = attribute.Key("pprof.profile.keep_frames") +) + +// PprofLocationIsFolded returns an attribute KeyValue conforming to the +// "pprof.location.is_folded" semantic conventions. It represents the provides an +// indication that multiple symbols map to this location's address, for example +// due to identical code folding by the linker. In that case the line information +// represents one of the multiple symbols. This field must be recomputed when the +// symbolization state of the profile changes. +func PprofLocationIsFolded(val bool) attribute.KeyValue { + return PprofLocationIsFoldedKey.Bool(val) +} + +// PprofMappingHasFilenames returns an attribute KeyValue conforming to the +// "pprof.mapping.has_filenames" semantic conventions. It represents the +// indicates that there are filenames related to this mapping. +func PprofMappingHasFilenames(val bool) attribute.KeyValue { + return PprofMappingHasFilenamesKey.Bool(val) +} + +// PprofMappingHasFunctions returns an attribute KeyValue conforming to the +// "pprof.mapping.has_functions" semantic conventions. It represents the +// indicates that there are functions related to this mapping. +func PprofMappingHasFunctions(val bool) attribute.KeyValue { + return PprofMappingHasFunctionsKey.Bool(val) +} + +// PprofMappingHasInlineFrames returns an attribute KeyValue conforming to the +// "pprof.mapping.has_inline_frames" semantic conventions. It represents the +// indicates that there are inline frames related to this mapping. +func PprofMappingHasInlineFrames(val bool) attribute.KeyValue { + return PprofMappingHasInlineFramesKey.Bool(val) +} + +// PprofMappingHasLineNumbers returns an attribute KeyValue conforming to the +// "pprof.mapping.has_line_numbers" semantic conventions. It represents the +// indicates that there are line numbers related to this mapping. +func PprofMappingHasLineNumbers(val bool) attribute.KeyValue { + return PprofMappingHasLineNumbersKey.Bool(val) +} + +// PprofProfileComment returns an attribute KeyValue conforming to the +// "pprof.profile.comment" semantic conventions. It represents the free-form text +// associated with the profile. This field should not be used to store any +// machine-readable information, it is only for human-friendly content. +func PprofProfileComment(val ...string) attribute.KeyValue { + return PprofProfileCommentKey.StringSlice(val) +} + +// PprofProfileDocURL returns an attribute KeyValue conforming to the +// "pprof.profile.doc_url" semantic conventions. It represents the documentation +// link for this profile type. +func PprofProfileDocURL(val string) attribute.KeyValue { + return PprofProfileDocURLKey.String(val) +} + +// PprofProfileDropFrames returns an attribute KeyValue conforming to the +// "pprof.profile.drop_frames" semantic conventions. It represents the frames +// with Function.function_name fully matching the regexp will be dropped from the +// samples, along with their successors. +func PprofProfileDropFrames(val string) attribute.KeyValue { + return PprofProfileDropFramesKey.String(val) +} + +// PprofProfileKeepFrames returns an attribute KeyValue conforming to the +// "pprof.profile.keep_frames" semantic conventions. It represents the frames +// with Function.function_name fully matching the regexp will be kept, even if it +// matches drop_frames. +func PprofProfileKeepFrames(val string) attribute.KeyValue { + return PprofProfileKeepFramesKey.String(val) +} + +// Namespace: process +const ( + // ProcessArgsCountKey is the attribute Key conforming to the + // "process.args_count" semantic conventions. It represents the length of the + // process.command_args array. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 4 + // Note: This field can be useful for querying or performing bucket analysis on + // how many arguments were provided to start a process. More arguments may be an + // indication of suspicious activity. + ProcessArgsCountKey = attribute.Key("process.args_count") + + // ProcessCommandKey is the attribute Key conforming to the "process.command" + // semantic conventions. It represents the command used to launch the process + // (i.e. the command name). On Linux based systems, can be set to the zeroth + // string in `proc/[pid]/cmdline`. On Windows, can be set to the first parameter + // extracted from `GetCommandLineW`. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "cmd/otelcol" + ProcessCommandKey = attribute.Key("process.command") + + // ProcessCommandArgsKey is the attribute Key conforming to the + // "process.command_args" semantic conventions. It represents the all the + // command arguments (including the command/executable itself) as received by + // the process. On Linux-based systems (and some other Unixoid systems + // supporting procfs), can be set according to the list of null-delimited + // strings extracted from `proc/[pid]/cmdline`. For libc-based executables, this + // would be the full argv vector passed to `main`. SHOULD NOT be collected by + // default unless there is sanitization that excludes sensitive data. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "cmd/otecol", "--config=config.yaml" + ProcessCommandArgsKey = attribute.Key("process.command_args") + + // ProcessCommandLineKey is the attribute Key conforming to the + // "process.command_line" semantic conventions. It represents the full command + // used to launch the process as a single string representing the full command. + // On Windows, can be set to the result of `GetCommandLineW`. Do not set this if + // you have to assemble it just for monitoring; use `process.command_args` + // instead. SHOULD NOT be collected by default unless there is sanitization that + // excludes sensitive data. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "C:\cmd\otecol --config="my directory\config.yaml"" + ProcessCommandLineKey = attribute.Key("process.command_line") + + // ProcessContextSwitchTypeKey is the attribute Key conforming to the + // "process.context_switch.type" semantic conventions. It represents the + // specifies whether the context switches for this data point were voluntary or + // involuntary. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + ProcessContextSwitchTypeKey = attribute.Key("process.context_switch.type") + + // ProcessCreationTimeKey is the attribute Key conforming to the + // "process.creation.time" semantic conventions. It represents the date and time + // the process was created, in ISO 8601 format. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2023-11-21T09:25:34.853Z" + ProcessCreationTimeKey = attribute.Key("process.creation.time") + + // ProcessExecutableBuildIDGNUKey is the attribute Key conforming to the + // "process.executable.build_id.gnu" semantic conventions. It represents the GNU + // build ID as found in the `.note.gnu.build-id` ELF section (hex string). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "c89b11207f6479603b0d49bf291c092c2b719293" + ProcessExecutableBuildIDGNUKey = attribute.Key("process.executable.build_id.gnu") + + // ProcessExecutableBuildIDGoKey is the attribute Key conforming to the + // "process.executable.build_id.go" semantic conventions. It represents the Go + // build ID as retrieved by `go tool buildid `. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "foh3mEXu7BLZjsN9pOwG/kATcXlYVCDEFouRMQed_/WwRFB1hPo9LBkekthSPG/x8hMC8emW2cCjXD0_1aY" + ProcessExecutableBuildIDGoKey = attribute.Key("process.executable.build_id.go") + + // ProcessExecutableBuildIDHtlhashKey is the attribute Key conforming to the + // "process.executable.build_id.htlhash" semantic conventions. It represents the + // profiling specific build ID for executables. See the OTel specification for + // Profiles for more information. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "600DCAFE4A110000F2BF38C493F5FB92" + ProcessExecutableBuildIDHtlhashKey = attribute.Key("process.executable.build_id.htlhash") + + // ProcessExecutableNameKey is the attribute Key conforming to the + // "process.executable.name" semantic conventions. It represents the name of the + // process executable. On Linux based systems, this SHOULD be set to the base + // name of the target of `/proc/[pid]/exe`. On Windows, this SHOULD be set to + // the base name of `GetProcessImageFileNameW`. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "otelcol" + ProcessExecutableNameKey = attribute.Key("process.executable.name") + + // ProcessExecutablePathKey is the attribute Key conforming to the + // "process.executable.path" semantic conventions. It represents the full path + // to the process executable. On Linux based systems, can be set to the target + // of `proc/[pid]/exe`. On Windows, can be set to the result of + // `GetProcessImageFileNameW`. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/usr/bin/cmd/otelcol" + ProcessExecutablePathKey = attribute.Key("process.executable.path") + + // ProcessExitCodeKey is the attribute Key conforming to the "process.exit.code" + // semantic conventions. It represents the exit code of the process. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 127 + ProcessExitCodeKey = attribute.Key("process.exit.code") + + // ProcessExitTimeKey is the attribute Key conforming to the "process.exit.time" + // semantic conventions. It represents the date and time the process exited, in + // ISO 8601 format. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2023-11-21T09:26:12.315Z" + ProcessExitTimeKey = attribute.Key("process.exit.time") + + // ProcessGroupLeaderPIDKey is the attribute Key conforming to the + // "process.group_leader.pid" semantic conventions. It represents the PID of the + // process's group leader. This is also the process group ID (PGID) of the + // process. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 23 + ProcessGroupLeaderPIDKey = attribute.Key("process.group_leader.pid") + + // ProcessInteractiveKey is the attribute Key conforming to the + // "process.interactive" semantic conventions. It represents the whether the + // process is connected to an interactive shell. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + ProcessInteractiveKey = attribute.Key("process.interactive") + + // ProcessLinuxCgroupKey is the attribute Key conforming to the + // "process.linux.cgroup" semantic conventions. It represents the control group + // associated with the process. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "1:name=systemd:/user.slice/user-1000.slice/session-3.scope", + // "0::/user.slice/user-1000.slice/user@1000.service/tmux-spawn-0267755b-4639-4a27-90ed-f19f88e53748.scope" + // Note: Control groups (cgroups) are a kernel feature used to organize and + // manage process resources. This attribute provides the path(s) to the + // cgroup(s) associated with the process, which should match the contents of the + // [/proc/[PID]/cgroup] file. + // + // [/proc/[PID]/cgroup]: https://man7.org/linux/man-pages/man7/cgroups.7.html + ProcessLinuxCgroupKey = attribute.Key("process.linux.cgroup") + + // ProcessOwnerKey is the attribute Key conforming to the "process.owner" + // semantic conventions. It represents the username of the user that owns the + // process. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "root" + ProcessOwnerKey = attribute.Key("process.owner") + + // ProcessParentPIDKey is the attribute Key conforming to the + // "process.parent_pid" semantic conventions. It represents the parent Process + // identifier (PPID). + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 111 + ProcessParentPIDKey = attribute.Key("process.parent_pid") + + // ProcessPIDKey is the attribute Key conforming to the "process.pid" semantic + // conventions. It represents the process identifier (PID). + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1234 + ProcessPIDKey = attribute.Key("process.pid") + + // ProcessRealUserIDKey is the attribute Key conforming to the + // "process.real_user.id" semantic conventions. It represents the real user ID + // (RUID) of the process. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1000 + ProcessRealUserIDKey = attribute.Key("process.real_user.id") + + // ProcessRealUserNameKey is the attribute Key conforming to the + // "process.real_user.name" semantic conventions. It represents the username of + // the real user of the process. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "operator" + ProcessRealUserNameKey = attribute.Key("process.real_user.name") + + // ProcessRuntimeDescriptionKey is the attribute Key conforming to the + // "process.runtime.description" semantic conventions. It represents an + // additional description about the runtime of the process, for example a + // specific vendor customization of the runtime environment. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: Eclipse OpenJ9 Eclipse OpenJ9 VM openj9-0.21.0 + ProcessRuntimeDescriptionKey = attribute.Key("process.runtime.description") + + // ProcessRuntimeNameKey is the attribute Key conforming to the + // "process.runtime.name" semantic conventions. It represents the name of the + // runtime of this process. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "OpenJDK Runtime Environment" + ProcessRuntimeNameKey = attribute.Key("process.runtime.name") + + // ProcessRuntimeVersionKey is the attribute Key conforming to the + // "process.runtime.version" semantic conventions. It represents the version of + // the runtime of this process, as returned by the runtime without modification. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 14.0.2 + ProcessRuntimeVersionKey = attribute.Key("process.runtime.version") + + // ProcessSavedUserIDKey is the attribute Key conforming to the + // "process.saved_user.id" semantic conventions. It represents the saved user ID + // (SUID) of the process. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1002 + ProcessSavedUserIDKey = attribute.Key("process.saved_user.id") + + // ProcessSavedUserNameKey is the attribute Key conforming to the + // "process.saved_user.name" semantic conventions. It represents the username of + // the saved user. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "operator" + ProcessSavedUserNameKey = attribute.Key("process.saved_user.name") + + // ProcessSessionLeaderPIDKey is the attribute Key conforming to the + // "process.session_leader.pid" semantic conventions. It represents the PID of + // the process's session leader. This is also the session ID (SID) of the + // process. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 14 + ProcessSessionLeaderPIDKey = attribute.Key("process.session_leader.pid") + + // ProcessStateKey is the attribute Key conforming to the "process.state" + // semantic conventions. It represents the process state, e.g., + // [Linux Process State Codes]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "running" + // + // [Linux Process State Codes]: https://man7.org/linux/man-pages/man1/ps.1.html#PROCESS_STATE_CODES + ProcessStateKey = attribute.Key("process.state") + + // ProcessTitleKey is the attribute Key conforming to the "process.title" + // semantic conventions. It represents the process title (proctitle). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "cat /etc/hostname", "xfce4-session", "bash" + // Note: In many Unix-like systems, process title (proctitle), is the string + // that represents the name or command line of a running process, displayed by + // system monitoring tools like ps, top, and htop. + ProcessTitleKey = attribute.Key("process.title") + + // ProcessUserIDKey is the attribute Key conforming to the "process.user.id" + // semantic conventions. It represents the effective user ID (EUID) of the + // process. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 1001 + ProcessUserIDKey = attribute.Key("process.user.id") + + // ProcessUserNameKey is the attribute Key conforming to the "process.user.name" + // semantic conventions. It represents the username of the effective user of the + // process. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "root" + ProcessUserNameKey = attribute.Key("process.user.name") + + // ProcessVpidKey is the attribute Key conforming to the "process.vpid" semantic + // conventions. It represents the virtual process identifier. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 12 + // Note: The process ID within a PID namespace. This is not necessarily unique + // across all processes on the host but it is unique within the process + // namespace that the process exists within. + ProcessVpidKey = attribute.Key("process.vpid") + + // ProcessWorkingDirectoryKey is the attribute Key conforming to the + // "process.working_directory" semantic conventions. It represents the working + // directory of the process. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/root" + ProcessWorkingDirectoryKey = attribute.Key("process.working_directory") +) + +// ProcessArgsCount returns an attribute KeyValue conforming to the +// "process.args_count" semantic conventions. It represents the length of the +// process.command_args array. +func ProcessArgsCount(val int) attribute.KeyValue { + return ProcessArgsCountKey.Int(val) +} + +// ProcessCommand returns an attribute KeyValue conforming to the +// "process.command" semantic conventions. It represents the command used to +// launch the process (i.e. the command name). On Linux based systems, can be set +// to the zeroth string in `proc/[pid]/cmdline`. On Windows, can be set to the +// first parameter extracted from `GetCommandLineW`. +func ProcessCommand(val string) attribute.KeyValue { + return ProcessCommandKey.String(val) +} + +// ProcessCommandArgs returns an attribute KeyValue conforming to the +// "process.command_args" semantic conventions. It represents the all the command +// arguments (including the command/executable itself) as received by the +// process. On Linux-based systems (and some other Unixoid systems supporting +// procfs), can be set according to the list of null-delimited strings extracted +// from `proc/[pid]/cmdline`. For libc-based executables, this would be the full +// argv vector passed to `main`. SHOULD NOT be collected by default unless there +// is sanitization that excludes sensitive data. +func ProcessCommandArgs(val ...string) attribute.KeyValue { + return ProcessCommandArgsKey.StringSlice(val) +} + +// ProcessCommandLine returns an attribute KeyValue conforming to the +// "process.command_line" semantic conventions. It represents the full command +// used to launch the process as a single string representing the full command. +// On Windows, can be set to the result of `GetCommandLineW`. Do not set this if +// you have to assemble it just for monitoring; use `process.command_args` +// instead. SHOULD NOT be collected by default unless there is sanitization that +// excludes sensitive data. +func ProcessCommandLine(val string) attribute.KeyValue { + return ProcessCommandLineKey.String(val) +} + +// ProcessCreationTime returns an attribute KeyValue conforming to the +// "process.creation.time" semantic conventions. It represents the date and time +// the process was created, in ISO 8601 format. +func ProcessCreationTime(val string) attribute.KeyValue { + return ProcessCreationTimeKey.String(val) +} + +// ProcessEnvironmentVariable returns an attribute KeyValue conforming to the +// "process.environment_variable" semantic conventions. It represents the process +// environment variables, `` being the environment variable name, the value +// being the environment variable value. +func ProcessEnvironmentVariable(key string, val string) attribute.KeyValue { + return attribute.String("process.environment_variable."+key, val) +} + +// ProcessExecutableBuildIDGNU returns an attribute KeyValue conforming to the +// "process.executable.build_id.gnu" semantic conventions. It represents the GNU +// build ID as found in the `.note.gnu.build-id` ELF section (hex string). +func ProcessExecutableBuildIDGNU(val string) attribute.KeyValue { + return ProcessExecutableBuildIDGNUKey.String(val) +} + +// ProcessExecutableBuildIDGo returns an attribute KeyValue conforming to the +// "process.executable.build_id.go" semantic conventions. It represents the Go +// build ID as retrieved by `go tool buildid `. +func ProcessExecutableBuildIDGo(val string) attribute.KeyValue { + return ProcessExecutableBuildIDGoKey.String(val) +} + +// ProcessExecutableBuildIDHtlhash returns an attribute KeyValue conforming to +// the "process.executable.build_id.htlhash" semantic conventions. It represents +// the profiling specific build ID for executables. See the OTel specification +// for Profiles for more information. +func ProcessExecutableBuildIDHtlhash(val string) attribute.KeyValue { + return ProcessExecutableBuildIDHtlhashKey.String(val) +} + +// ProcessExecutableName returns an attribute KeyValue conforming to the +// "process.executable.name" semantic conventions. It represents the name of the +// process executable. On Linux based systems, this SHOULD be set to the base +// name of the target of `/proc/[pid]/exe`. On Windows, this SHOULD be set to the +// base name of `GetProcessImageFileNameW`. +func ProcessExecutableName(val string) attribute.KeyValue { + return ProcessExecutableNameKey.String(val) +} + +// ProcessExecutablePath returns an attribute KeyValue conforming to the +// "process.executable.path" semantic conventions. It represents the full path to +// the process executable. On Linux based systems, can be set to the target of +// `proc/[pid]/exe`. On Windows, can be set to the result of +// `GetProcessImageFileNameW`. +func ProcessExecutablePath(val string) attribute.KeyValue { + return ProcessExecutablePathKey.String(val) +} + +// ProcessExitCode returns an attribute KeyValue conforming to the +// "process.exit.code" semantic conventions. It represents the exit code of the +// process. +func ProcessExitCode(val int) attribute.KeyValue { + return ProcessExitCodeKey.Int(val) +} + +// ProcessExitTime returns an attribute KeyValue conforming to the +// "process.exit.time" semantic conventions. It represents the date and time the +// process exited, in ISO 8601 format. +func ProcessExitTime(val string) attribute.KeyValue { + return ProcessExitTimeKey.String(val) +} + +// ProcessGroupLeaderPID returns an attribute KeyValue conforming to the +// "process.group_leader.pid" semantic conventions. It represents the PID of the +// process's group leader. This is also the process group ID (PGID) of the +// process. +func ProcessGroupLeaderPID(val int) attribute.KeyValue { + return ProcessGroupLeaderPIDKey.Int(val) +} + +// ProcessInteractive returns an attribute KeyValue conforming to the +// "process.interactive" semantic conventions. It represents the whether the +// process is connected to an interactive shell. +func ProcessInteractive(val bool) attribute.KeyValue { + return ProcessInteractiveKey.Bool(val) +} + +// ProcessLinuxCgroup returns an attribute KeyValue conforming to the +// "process.linux.cgroup" semantic conventions. It represents the control group +// associated with the process. +func ProcessLinuxCgroup(val string) attribute.KeyValue { + return ProcessLinuxCgroupKey.String(val) +} + +// ProcessOwner returns an attribute KeyValue conforming to the "process.owner" +// semantic conventions. It represents the username of the user that owns the +// process. +func ProcessOwner(val string) attribute.KeyValue { + return ProcessOwnerKey.String(val) +} + +// ProcessParentPID returns an attribute KeyValue conforming to the +// "process.parent_pid" semantic conventions. It represents the parent Process +// identifier (PPID). +func ProcessParentPID(val int) attribute.KeyValue { + return ProcessParentPIDKey.Int(val) +} + +// ProcessPID returns an attribute KeyValue conforming to the "process.pid" +// semantic conventions. It represents the process identifier (PID). +func ProcessPID(val int) attribute.KeyValue { + return ProcessPIDKey.Int(val) +} + +// ProcessRealUserID returns an attribute KeyValue conforming to the +// "process.real_user.id" semantic conventions. It represents the real user ID +// (RUID) of the process. +func ProcessRealUserID(val int) attribute.KeyValue { + return ProcessRealUserIDKey.Int(val) +} + +// ProcessRealUserName returns an attribute KeyValue conforming to the +// "process.real_user.name" semantic conventions. It represents the username of +// the real user of the process. +func ProcessRealUserName(val string) attribute.KeyValue { + return ProcessRealUserNameKey.String(val) +} + +// ProcessRuntimeDescription returns an attribute KeyValue conforming to the +// "process.runtime.description" semantic conventions. It represents an +// additional description about the runtime of the process, for example a +// specific vendor customization of the runtime environment. +func ProcessRuntimeDescription(val string) attribute.KeyValue { + return ProcessRuntimeDescriptionKey.String(val) +} + +// ProcessRuntimeName returns an attribute KeyValue conforming to the +// "process.runtime.name" semantic conventions. It represents the name of the +// runtime of this process. +func ProcessRuntimeName(val string) attribute.KeyValue { + return ProcessRuntimeNameKey.String(val) +} + +// ProcessRuntimeVersion returns an attribute KeyValue conforming to the +// "process.runtime.version" semantic conventions. It represents the version of +// the runtime of this process, as returned by the runtime without modification. +func ProcessRuntimeVersion(val string) attribute.KeyValue { + return ProcessRuntimeVersionKey.String(val) +} + +// ProcessSavedUserID returns an attribute KeyValue conforming to the +// "process.saved_user.id" semantic conventions. It represents the saved user ID +// (SUID) of the process. +func ProcessSavedUserID(val int) attribute.KeyValue { + return ProcessSavedUserIDKey.Int(val) +} + +// ProcessSavedUserName returns an attribute KeyValue conforming to the +// "process.saved_user.name" semantic conventions. It represents the username of +// the saved user. +func ProcessSavedUserName(val string) attribute.KeyValue { + return ProcessSavedUserNameKey.String(val) +} + +// ProcessSessionLeaderPID returns an attribute KeyValue conforming to the +// "process.session_leader.pid" semantic conventions. It represents the PID of +// the process's session leader. This is also the session ID (SID) of the +// process. +func ProcessSessionLeaderPID(val int) attribute.KeyValue { + return ProcessSessionLeaderPIDKey.Int(val) +} + +// ProcessTitle returns an attribute KeyValue conforming to the "process.title" +// semantic conventions. It represents the process title (proctitle). +func ProcessTitle(val string) attribute.KeyValue { + return ProcessTitleKey.String(val) +} + +// ProcessUserID returns an attribute KeyValue conforming to the +// "process.user.id" semantic conventions. It represents the effective user ID +// (EUID) of the process. +func ProcessUserID(val int) attribute.KeyValue { + return ProcessUserIDKey.Int(val) +} + +// ProcessUserName returns an attribute KeyValue conforming to the +// "process.user.name" semantic conventions. It represents the username of the +// effective user of the process. +func ProcessUserName(val string) attribute.KeyValue { + return ProcessUserNameKey.String(val) +} + +// ProcessVpid returns an attribute KeyValue conforming to the "process.vpid" +// semantic conventions. It represents the virtual process identifier. +func ProcessVpid(val int) attribute.KeyValue { + return ProcessVpidKey.Int(val) +} + +// ProcessWorkingDirectory returns an attribute KeyValue conforming to the +// "process.working_directory" semantic conventions. It represents the working +// directory of the process. +func ProcessWorkingDirectory(val string) attribute.KeyValue { + return ProcessWorkingDirectoryKey.String(val) +} + +// Enum values for process.context_switch.type +var ( + // voluntary + // Stability: development + ProcessContextSwitchTypeVoluntary = ProcessContextSwitchTypeKey.String("voluntary") + // involuntary + // Stability: development + ProcessContextSwitchTypeInvoluntary = ProcessContextSwitchTypeKey.String("involuntary") +) + +// Enum values for process.state +var ( + // running + // Stability: development + ProcessStateRunning = ProcessStateKey.String("running") + // sleeping + // Stability: development + ProcessStateSleeping = ProcessStateKey.String("sleeping") + // stopped + // Stability: development + ProcessStateStopped = ProcessStateKey.String("stopped") + // defunct + // Stability: development + ProcessStateDefunct = ProcessStateKey.String("defunct") +) + +// Namespace: profile +const ( + // ProfileFrameTypeKey is the attribute Key conforming to the + // "profile.frame.type" semantic conventions. It represents the describes the + // interpreter or compiler of a single frame. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "cpython" + ProfileFrameTypeKey = attribute.Key("profile.frame.type") +) + +// Enum values for profile.frame.type +var ( + // [.NET] + // + // Stability: development + // + // [.NET]: https://wikipedia.org/wiki/.NET + ProfileFrameTypeDotnet = ProfileFrameTypeKey.String("dotnet") + // [JVM] + // + // Stability: development + // + // [JVM]: https://wikipedia.org/wiki/Java_virtual_machine + ProfileFrameTypeJVM = ProfileFrameTypeKey.String("jvm") + // [Kernel] + // + // Stability: development + // + // [Kernel]: https://wikipedia.org/wiki/Kernel_(operating_system) + ProfileFrameTypeKernel = ProfileFrameTypeKey.String("kernel") + // Can be one of but not limited to [C], [C++], [Go] or [Rust]. If possible, a + // more precise value MUST be used. + // + // Stability: development + // + // [C]: https://wikipedia.org/wiki/C_(programming_language) + // [C++]: https://wikipedia.org/wiki/C%2B%2B + // [Go]: https://wikipedia.org/wiki/Go_(programming_language) + // [Rust]: https://wikipedia.org/wiki/Rust_(programming_language) + ProfileFrameTypeNative = ProfileFrameTypeKey.String("native") + // [Perl] + // + // Stability: development + // + // [Perl]: https://wikipedia.org/wiki/Perl + ProfileFrameTypePerl = ProfileFrameTypeKey.String("perl") + // [PHP] + // + // Stability: development + // + // [PHP]: https://wikipedia.org/wiki/PHP + ProfileFrameTypePHP = ProfileFrameTypeKey.String("php") + // [Python] + // + // Stability: development + // + // [Python]: https://wikipedia.org/wiki/Python_(programming_language) + ProfileFrameTypeCpython = ProfileFrameTypeKey.String("cpython") + // [Ruby] + // + // Stability: development + // + // [Ruby]: https://wikipedia.org/wiki/Ruby_(programming_language) + ProfileFrameTypeRuby = ProfileFrameTypeKey.String("ruby") + // [V8JS] + // + // Stability: development + // + // [V8JS]: https://wikipedia.org/wiki/V8_(JavaScript_engine) + ProfileFrameTypeV8JS = ProfileFrameTypeKey.String("v8js") + // [Erlang] + // + // Stability: development + // + // [Erlang]: https://en.wikipedia.org/wiki/BEAM_(Erlang_virtual_machine) + ProfileFrameTypeBeam = ProfileFrameTypeKey.String("beam") + // [Go], + // + // Stability: development + // + // [Go]: https://wikipedia.org/wiki/Go_(programming_language) + ProfileFrameTypeGo = ProfileFrameTypeKey.String("go") + // [Rust] + // + // Stability: development + // + // [Rust]: https://wikipedia.org/wiki/Rust_(programming_language) + ProfileFrameTypeRust = ProfileFrameTypeKey.String("rust") +) + +// Namespace: rpc +const ( + // RPCMessageCompressedSizeKey is the attribute Key conforming to the + // "rpc.message.compressed_size" semantic conventions. It represents the + // compressed size of the message in bytes. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + RPCMessageCompressedSizeKey = attribute.Key("rpc.message.compressed_size") + + // RPCMessageIDKey is the attribute Key conforming to the "rpc.message.id" + // semantic conventions. It MUST be calculated as two different counters + // starting from `1` one for sent messages and one for received message.. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: This way we guarantee that the values will be consistent between + // different implementations. + RPCMessageIDKey = attribute.Key("rpc.message.id") + + // RPCMessageTypeKey is the attribute Key conforming to the "rpc.message.type" + // semantic conventions. It represents the whether this is a received or sent + // message. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + RPCMessageTypeKey = attribute.Key("rpc.message.type") + + // RPCMessageUncompressedSizeKey is the attribute Key conforming to the + // "rpc.message.uncompressed_size" semantic conventions. It represents the + // uncompressed size of the message in bytes. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + RPCMessageUncompressedSizeKey = attribute.Key("rpc.message.uncompressed_size") + + // RPCMethodKey is the attribute Key conforming to the "rpc.method" semantic + // conventions. It represents the fully-qualified logical name of the method + // from the RPC interface perspective. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "com.example.ExampleService/exampleMethod", "EchoService/Echo", + // "_OTHER" + // Note: The method name MAY have unbounded cardinality in edge or error cases. + // + // Some RPC frameworks or libraries provide a fixed set of recognized methods + // for client stubs and server implementations. Instrumentations for such + // frameworks MUST set this attribute to the original method name only + // when the method is recognized by the framework or library. + // + // When the method is not recognized, for example, when the server receives + // a request for a method that is not predefined on the server, or when + // instrumentation is not able to reliably detect if the method is predefined, + // the attribute MUST be set to `_OTHER`. In such cases, tracing + // instrumentations MUST also set `rpc.method_original` attribute to + // the original method value. + // + // If the RPC instrumentation could end up converting valid RPC methods to + // `_OTHER`, then it SHOULD provide a way to configure the list of recognized + // RPC methods. + // + // The `rpc.method` can be different from the name of any implementing + // method/function. + // The `code.function.name` attribute may be used to record the fully-qualified + // method actually executing the call on the server side, or the + // RPC client stub method on the client side. + RPCMethodKey = attribute.Key("rpc.method") + + // RPCMethodOriginalKey is the attribute Key conforming to the + // "rpc.method_original" semantic conventions. It represents the original name + // of the method used by the client. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "com.myservice.EchoService/catchAll", + // "com.myservice.EchoService/unknownMethod", "InvalidMethod" + RPCMethodOriginalKey = attribute.Key("rpc.method_original") + + // RPCResponseStatusCodeKey is the attribute Key conforming to the + // "rpc.response.status_code" semantic conventions. It represents the status + // code of the RPC returned by the RPC server or generated by the client. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "OK", "DEADLINE_EXCEEDED", "-32602" + // Note: Usually it represents an error code, but may also represent partial + // success, warning, or differentiate between various types of successful + // outcomes. + // Semantic conventions for individual RPC frameworks SHOULD document what + // `rpc.response.status_code` means in the context of that system and which + // values are considered to represent errors. + RPCResponseStatusCodeKey = attribute.Key("rpc.response.status_code") + + // RPCSystemNameKey is the attribute Key conforming to the "rpc.system.name" + // semantic conventions. It represents the Remote Procedure Call (RPC) system. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: The client and server RPC systems may differ for the same RPC + // interaction. For example, a client may use Apache Dubbo or Connect RPC to + // communicate with a server that uses gRPC since both protocols provide + // compatibility with gRPC. + RPCSystemNameKey = attribute.Key("rpc.system.name") +) + +// RPCMessageCompressedSize returns an attribute KeyValue conforming to the +// "rpc.message.compressed_size" semantic conventions. It represents the +// compressed size of the message in bytes. +func RPCMessageCompressedSize(val int) attribute.KeyValue { + return RPCMessageCompressedSizeKey.Int(val) +} + +// RPCMessageID returns an attribute KeyValue conforming to the "rpc.message.id" +// semantic conventions. It MUST be calculated as two different counters starting +// from `1` one for sent messages and one for received message.. +func RPCMessageID(val int) attribute.KeyValue { + return RPCMessageIDKey.Int(val) +} + +// RPCMessageUncompressedSize returns an attribute KeyValue conforming to the +// "rpc.message.uncompressed_size" semantic conventions. It represents the +// uncompressed size of the message in bytes. +func RPCMessageUncompressedSize(val int) attribute.KeyValue { + return RPCMessageUncompressedSizeKey.Int(val) +} + +// RPCMethod returns an attribute KeyValue conforming to the "rpc.method" +// semantic conventions. It represents the fully-qualified logical name of the +// method from the RPC interface perspective. +func RPCMethod(val string) attribute.KeyValue { + return RPCMethodKey.String(val) +} + +// RPCMethodOriginal returns an attribute KeyValue conforming to the +// "rpc.method_original" semantic conventions. It represents the original name of +// the method used by the client. +func RPCMethodOriginal(val string) attribute.KeyValue { + return RPCMethodOriginalKey.String(val) +} + +// RPCRequestMetadata returns an attribute KeyValue conforming to the +// "rpc.request.metadata" semantic conventions. It represents the RPC request +// metadata, `` being the normalized RPC metadata key (lowercase), the value +// being the metadata values. +func RPCRequestMetadata(key string, val ...string) attribute.KeyValue { + return attribute.StringSlice("rpc.request.metadata."+key, val) +} + +// RPCResponseMetadata returns an attribute KeyValue conforming to the +// "rpc.response.metadata" semantic conventions. It represents the RPC response +// metadata, `` being the normalized RPC metadata key (lowercase), the value +// being the metadata values. +func RPCResponseMetadata(key string, val ...string) attribute.KeyValue { + return attribute.StringSlice("rpc.response.metadata."+key, val) +} + +// RPCResponseStatusCode returns an attribute KeyValue conforming to the +// "rpc.response.status_code" semantic conventions. It represents the status code +// of the RPC returned by the RPC server or generated by the client. +func RPCResponseStatusCode(val string) attribute.KeyValue { + return RPCResponseStatusCodeKey.String(val) +} + +// Enum values for rpc.message.type +var ( + // sent + // Stability: development + RPCMessageTypeSent = RPCMessageTypeKey.String("SENT") + // received + // Stability: development + RPCMessageTypeReceived = RPCMessageTypeKey.String("RECEIVED") +) + +// Enum values for rpc.system.name +var ( + // [gRPC] + // Stability: development + // + // [gRPC]: https://grpc.io/ + RPCSystemNameGRPC = RPCSystemNameKey.String("grpc") + // [Apache Dubbo] + // Stability: development + // + // [Apache Dubbo]: https://dubbo.apache.org/ + RPCSystemNameDubbo = RPCSystemNameKey.String("dubbo") + // [Connect RPC] + // Stability: development + // + // [Connect RPC]: https://connectrpc.com/ + RPCSystemNameConnectrpc = RPCSystemNameKey.String("connectrpc") + // [JSON-RPC] + // Stability: development + // + // [JSON-RPC]: https://www.jsonrpc.org/ + RPCSystemNameJSONRPC = RPCSystemNameKey.String("jsonrpc") +) + +// Namespace: security_rule +const ( + // SecurityRuleCategoryKey is the attribute Key conforming to the + // "security_rule.category" semantic conventions. It represents a categorization + // value keyword used by the entity using the rule for detection of this event. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Attempted Information Leak" + SecurityRuleCategoryKey = attribute.Key("security_rule.category") + + // SecurityRuleDescriptionKey is the attribute Key conforming to the + // "security_rule.description" semantic conventions. It represents the + // description of the rule generating the event. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Block requests to public DNS over HTTPS / TLS protocols" + SecurityRuleDescriptionKey = attribute.Key("security_rule.description") + + // SecurityRuleLicenseKey is the attribute Key conforming to the + // "security_rule.license" semantic conventions. It represents the name of the + // license under which the rule used to generate this event is made available. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Apache 2.0" + SecurityRuleLicenseKey = attribute.Key("security_rule.license") + + // SecurityRuleNameKey is the attribute Key conforming to the + // "security_rule.name" semantic conventions. It represents the name of the rule + // or signature generating the event. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "BLOCK_DNS_over_TLS" + SecurityRuleNameKey = attribute.Key("security_rule.name") + + // SecurityRuleReferenceKey is the attribute Key conforming to the + // "security_rule.reference" semantic conventions. It represents the reference + // URL to additional information about the rule used to generate this event. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "https://en.wikipedia.org/wiki/DNS_over_TLS" + // Note: The URL can point to the vendor’s documentation about the rule. If + // that’s not available, it can also be a link to a more general page + // describing this type of alert. + SecurityRuleReferenceKey = attribute.Key("security_rule.reference") + + // SecurityRuleRulesetNameKey is the attribute Key conforming to the + // "security_rule.ruleset.name" semantic conventions. It represents the name of + // the ruleset, policy, group, or parent category in which the rule used to + // generate this event is a member. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Standard_Protocol_Filters" + SecurityRuleRulesetNameKey = attribute.Key("security_rule.ruleset.name") + + // SecurityRuleUUIDKey is the attribute Key conforming to the + // "security_rule.uuid" semantic conventions. It represents a rule ID that is + // unique within the scope of a set or group of agents, observers, or other + // entities using the rule for detection of this event. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "550e8400-e29b-41d4-a716-446655440000", "1100110011" + SecurityRuleUUIDKey = attribute.Key("security_rule.uuid") + + // SecurityRuleVersionKey is the attribute Key conforming to the + // "security_rule.version" semantic conventions. It represents the version / + // revision of the rule being used for analysis. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "1.0.0" + SecurityRuleVersionKey = attribute.Key("security_rule.version") +) + +// SecurityRuleCategory returns an attribute KeyValue conforming to the +// "security_rule.category" semantic conventions. It represents a categorization +// value keyword used by the entity using the rule for detection of this event. +func SecurityRuleCategory(val string) attribute.KeyValue { + return SecurityRuleCategoryKey.String(val) +} + +// SecurityRuleDescription returns an attribute KeyValue conforming to the +// "security_rule.description" semantic conventions. It represents the +// description of the rule generating the event. +func SecurityRuleDescription(val string) attribute.KeyValue { + return SecurityRuleDescriptionKey.String(val) +} + +// SecurityRuleLicense returns an attribute KeyValue conforming to the +// "security_rule.license" semantic conventions. It represents the name of the +// license under which the rule used to generate this event is made available. +func SecurityRuleLicense(val string) attribute.KeyValue { + return SecurityRuleLicenseKey.String(val) +} + +// SecurityRuleName returns an attribute KeyValue conforming to the +// "security_rule.name" semantic conventions. It represents the name of the rule +// or signature generating the event. +func SecurityRuleName(val string) attribute.KeyValue { + return SecurityRuleNameKey.String(val) +} + +// SecurityRuleReference returns an attribute KeyValue conforming to the +// "security_rule.reference" semantic conventions. It represents the reference +// URL to additional information about the rule used to generate this event. +func SecurityRuleReference(val string) attribute.KeyValue { + return SecurityRuleReferenceKey.String(val) +} + +// SecurityRuleRulesetName returns an attribute KeyValue conforming to the +// "security_rule.ruleset.name" semantic conventions. It represents the name of +// the ruleset, policy, group, or parent category in which the rule used to +// generate this event is a member. +func SecurityRuleRulesetName(val string) attribute.KeyValue { + return SecurityRuleRulesetNameKey.String(val) +} + +// SecurityRuleUUID returns an attribute KeyValue conforming to the +// "security_rule.uuid" semantic conventions. It represents a rule ID that is +// unique within the scope of a set or group of agents, observers, or other +// entities using the rule for detection of this event. +func SecurityRuleUUID(val string) attribute.KeyValue { + return SecurityRuleUUIDKey.String(val) +} + +// SecurityRuleVersion returns an attribute KeyValue conforming to the +// "security_rule.version" semantic conventions. It represents the version / +// revision of the rule being used for analysis. +func SecurityRuleVersion(val string) attribute.KeyValue { + return SecurityRuleVersionKey.String(val) +} + +// Namespace: server +const ( + // ServerAddressKey is the attribute Key conforming to the "server.address" + // semantic conventions. It represents the server domain name if available + // without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "example.com", "10.1.2.80", "/tmp/my.sock" + // Note: When observed from the client side, and when communicating through an + // intermediary, `server.address` SHOULD represent the server address behind any + // intermediaries, for example proxies, if it's available. + ServerAddressKey = attribute.Key("server.address") + + // ServerPortKey is the attribute Key conforming to the "server.port" semantic + // conventions. It represents the server port number. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: 80, 8080, 443 + // Note: When observed from the client side, and when communicating through an + // intermediary, `server.port` SHOULD represent the server port behind any + // intermediaries, for example proxies, if it's available. + ServerPortKey = attribute.Key("server.port") +) + +// ServerAddress returns an attribute KeyValue conforming to the "server.address" +// semantic conventions. It represents the server domain name if available +// without reverse DNS lookup; otherwise, IP address or Unix domain socket name. +func ServerAddress(val string) attribute.KeyValue { + return ServerAddressKey.String(val) +} + +// ServerPort returns an attribute KeyValue conforming to the "server.port" +// semantic conventions. It represents the server port number. +func ServerPort(val int) attribute.KeyValue { + return ServerPortKey.Int(val) +} + +// Namespace: service +const ( + // ServiceInstanceIDKey is the attribute Key conforming to the + // "service.instance.id" semantic conventions. It represents the string ID of + // the service instance. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "627cc493-f310-47de-96bd-71410b7dec09" + // Note: MUST be unique for each instance of the same + // `service.namespace,service.name` pair (in other words + // `service.namespace,service.name,service.instance.id` triplet MUST be globally + // unique). The ID helps to + // distinguish instances of the same service that exist at the same time (e.g. + // instances of a horizontally scaled + // service). + // + // Implementations, such as SDKs, are recommended to generate a random Version 1 + // or Version 4 [RFC + // 4122] UUID, but are free to use an inherent unique ID as + // the source of + // this value if stability is desirable. In that case, the ID SHOULD be used as + // source of a UUID Version 5 and + // SHOULD use the following UUID as the namespace: + // `4d63009a-8d0f-11ee-aad7-4c796ed8e320`. + // + // UUIDs are typically recommended, as only an opaque value for the purposes of + // identifying a service instance is + // needed. Similar to what can be seen in the man page for the + // [`/etc/machine-id`] file, the underlying + // data, such as pod name and namespace should be treated as confidential, being + // the user's choice to expose it + // or not via another resource attribute. + // + // For applications running behind an application server (like unicorn), we do + // not recommend using one identifier + // for all processes participating in the application. Instead, it's recommended + // each division (e.g. a worker + // thread in unicorn) to have its own instance.id. + // + // It's not recommended for a Collector to set `service.instance.id` if it can't + // unambiguously determine the + // service instance that is generating that telemetry. For instance, creating an + // UUID based on `pod.name` will + // likely be wrong, as the Collector might not know from which container within + // that pod the telemetry originated. + // However, Collectors can set the `service.instance.id` if they can + // unambiguously determine the service instance + // for that telemetry. This is typically the case for scraping receivers, as + // they know the target address and + // port. + // + // [RFC + // 4122]: https://www.ietf.org/rfc/rfc4122.txt + // [`/etc/machine-id`]: https://www.freedesktop.org/software/systemd/man/latest/machine-id.html + ServiceInstanceIDKey = attribute.Key("service.instance.id") + + // ServiceNameKey is the attribute Key conforming to the "service.name" semantic + // conventions. It represents the logical name of the service. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "shoppingcart" + // Note: MUST be the same for all instances of horizontally scaled services. If + // the value was not specified, SDKs MUST fallback to `unknown_service:` + // concatenated with [`process.executable.name`], e.g. `unknown_service:bash`. + // If `process.executable.name` is not available, the value MUST be set to + // `unknown_service`. + // + // [`process.executable.name`]: process.md + ServiceNameKey = attribute.Key("service.name") + + // ServiceNamespaceKey is the attribute Key conforming to the + // "service.namespace" semantic conventions. It represents a namespace for + // `service.name`. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Shop" + // Note: A string value having a meaning that helps to distinguish a group of + // services, for example the team name that owns a group of services. + // `service.name` is expected to be unique within the same namespace. If + // `service.namespace` is not specified in the Resource then `service.name` is + // expected to be unique for all services that have no explicit namespace + // defined (so the empty/unspecified namespace is simply one more valid + // namespace). Zero-length namespace string is assumed equal to unspecified + // namespace. + ServiceNamespaceKey = attribute.Key("service.namespace") + + // ServicePeerNameKey is the attribute Key conforming to the "service.peer.name" + // semantic conventions. It represents the logical name of the service on the + // other side of the connection. SHOULD be equal to the actual [`service.name`] + // resource attribute of the remote service if any. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "shoppingcart" + // + // [`service.name`]: /docs/resource/README.md#service + ServicePeerNameKey = attribute.Key("service.peer.name") + + // ServicePeerNamespaceKey is the attribute Key conforming to the + // "service.peer.namespace" semantic conventions. It represents the logical + // namespace of the service on the other side of the connection. SHOULD be equal + // to the actual [`service.namespace`] resource attribute of the remote service + // if any. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Shop" + // + // [`service.namespace`]: /docs/resource/README.md#service + ServicePeerNamespaceKey = attribute.Key("service.peer.namespace") + + // ServiceVersionKey is the attribute Key conforming to the "service.version" + // semantic conventions. It represents the version string of the service + // component. The format is not defined by these conventions. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "2.0.0", "a01dbef8a" + ServiceVersionKey = attribute.Key("service.version") +) + +// ServiceInstanceID returns an attribute KeyValue conforming to the +// "service.instance.id" semantic conventions. It represents the string ID of the +// service instance. +func ServiceInstanceID(val string) attribute.KeyValue { + return ServiceInstanceIDKey.String(val) +} + +// ServiceName returns an attribute KeyValue conforming to the "service.name" +// semantic conventions. It represents the logical name of the service. +func ServiceName(val string) attribute.KeyValue { + return ServiceNameKey.String(val) +} + +// ServiceNamespace returns an attribute KeyValue conforming to the +// "service.namespace" semantic conventions. It represents a namespace for +// `service.name`. +func ServiceNamespace(val string) attribute.KeyValue { + return ServiceNamespaceKey.String(val) +} + +// ServicePeerName returns an attribute KeyValue conforming to the +// "service.peer.name" semantic conventions. It represents the logical name of +// the service on the other side of the connection. SHOULD be equal to the actual +// [`service.name`] resource attribute of the remote service if any. +// +// [`service.name`]: /docs/resource/README.md#service +func ServicePeerName(val string) attribute.KeyValue { + return ServicePeerNameKey.String(val) +} + +// ServicePeerNamespace returns an attribute KeyValue conforming to the +// "service.peer.namespace" semantic conventions. It represents the logical +// namespace of the service on the other side of the connection. SHOULD be equal +// to the actual [`service.namespace`] resource attribute of the remote service +// if any. +// +// [`service.namespace`]: /docs/resource/README.md#service +func ServicePeerNamespace(val string) attribute.KeyValue { + return ServicePeerNamespaceKey.String(val) +} + +// ServiceVersion returns an attribute KeyValue conforming to the +// "service.version" semantic conventions. It represents the version string of +// the service component. The format is not defined by these conventions. +func ServiceVersion(val string) attribute.KeyValue { + return ServiceVersionKey.String(val) +} + +// Namespace: session +const ( + // SessionIDKey is the attribute Key conforming to the "session.id" semantic + // conventions. It represents a unique id to identify a session. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 00112233-4455-6677-8899-aabbccddeeff + SessionIDKey = attribute.Key("session.id") + + // SessionPreviousIDKey is the attribute Key conforming to the + // "session.previous_id" semantic conventions. It represents the previous + // `session.id` for this user, when known. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 00112233-4455-6677-8899-aabbccddeeff + SessionPreviousIDKey = attribute.Key("session.previous_id") +) + +// SessionID returns an attribute KeyValue conforming to the "session.id" +// semantic conventions. It represents a unique id to identify a session. +func SessionID(val string) attribute.KeyValue { + return SessionIDKey.String(val) +} + +// SessionPreviousID returns an attribute KeyValue conforming to the +// "session.previous_id" semantic conventions. It represents the previous +// `session.id` for this user, when known. +func SessionPreviousID(val string) attribute.KeyValue { + return SessionPreviousIDKey.String(val) +} + +// Namespace: signalr +const ( + // SignalRConnectionStatusKey is the attribute Key conforming to the + // "signalr.connection.status" semantic conventions. It represents the signalR + // HTTP connection closure status. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "app_shutdown", "timeout" + SignalRConnectionStatusKey = attribute.Key("signalr.connection.status") + + // SignalRTransportKey is the attribute Key conforming to the + // "signalr.transport" semantic conventions. It represents the + // [SignalR transport type]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "web_sockets", "long_polling" + // + // [SignalR transport type]: https://github.com/dotnet/aspnetcore/blob/main/src/SignalR/docs/specs/TransportProtocols.md + SignalRTransportKey = attribute.Key("signalr.transport") +) + +// Enum values for signalr.connection.status +var ( + // The connection was closed normally. + // Stability: stable + SignalRConnectionStatusNormalClosure = SignalRConnectionStatusKey.String("normal_closure") + // The connection was closed due to a timeout. + // Stability: stable + SignalRConnectionStatusTimeout = SignalRConnectionStatusKey.String("timeout") + // The connection was closed because the app is shutting down. + // Stability: stable + SignalRConnectionStatusAppShutdown = SignalRConnectionStatusKey.String("app_shutdown") +) + +// Enum values for signalr.transport +var ( + // ServerSentEvents protocol + // Stability: stable + SignalRTransportServerSentEvents = SignalRTransportKey.String("server_sent_events") + // LongPolling protocol + // Stability: stable + SignalRTransportLongPolling = SignalRTransportKey.String("long_polling") + // WebSockets protocol + // Stability: stable + SignalRTransportWebSockets = SignalRTransportKey.String("web_sockets") +) + +// Namespace: source +const ( + // SourceAddressKey is the attribute Key conforming to the "source.address" + // semantic conventions. It represents the source address - domain name if + // available without reverse DNS lookup; otherwise, IP address or Unix domain + // socket name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "source.example.com", "10.1.2.80", "/tmp/my.sock" + // Note: When observed from the destination side, and when communicating through + // an intermediary, `source.address` SHOULD represent the source address behind + // any intermediaries, for example proxies, if it's available. + SourceAddressKey = attribute.Key("source.address") + + // SourcePortKey is the attribute Key conforming to the "source.port" semantic + // conventions. It represents the source port number. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 3389, 2888 + SourcePortKey = attribute.Key("source.port") +) + +// SourceAddress returns an attribute KeyValue conforming to the "source.address" +// semantic conventions. It represents the source address - domain name if +// available without reverse DNS lookup; otherwise, IP address or Unix domain +// socket name. +func SourceAddress(val string) attribute.KeyValue { + return SourceAddressKey.String(val) +} + +// SourcePort returns an attribute KeyValue conforming to the "source.port" +// semantic conventions. It represents the source port number. +func SourcePort(val int) attribute.KeyValue { + return SourcePortKey.Int(val) +} + +// Namespace: system +const ( + // SystemDeviceKey is the attribute Key conforming to the "system.device" + // semantic conventions. It represents the device identifier. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "(identifier)" + SystemDeviceKey = attribute.Key("system.device") + + // SystemFilesystemModeKey is the attribute Key conforming to the + // "system.filesystem.mode" semantic conventions. It represents the filesystem + // mode. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "rw, ro" + SystemFilesystemModeKey = attribute.Key("system.filesystem.mode") + + // SystemFilesystemMountpointKey is the attribute Key conforming to the + // "system.filesystem.mountpoint" semantic conventions. It represents the + // filesystem mount path. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/mnt/data" + SystemFilesystemMountpointKey = attribute.Key("system.filesystem.mountpoint") + + // SystemFilesystemStateKey is the attribute Key conforming to the + // "system.filesystem.state" semantic conventions. It represents the filesystem + // state. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "used" + SystemFilesystemStateKey = attribute.Key("system.filesystem.state") + + // SystemFilesystemTypeKey is the attribute Key conforming to the + // "system.filesystem.type" semantic conventions. It represents the filesystem + // type. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "ext4" + SystemFilesystemTypeKey = attribute.Key("system.filesystem.type") + + // SystemMemoryLinuxSlabStateKey is the attribute Key conforming to the + // "system.memory.linux.slab.state" semantic conventions. It represents the + // Linux Slab memory state. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "reclaimable", "unreclaimable" + SystemMemoryLinuxSlabStateKey = attribute.Key("system.memory.linux.slab.state") + + // SystemMemoryStateKey is the attribute Key conforming to the + // "system.memory.state" semantic conventions. It represents the memory state. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "free", "cached" + SystemMemoryStateKey = attribute.Key("system.memory.state") + + // SystemPagingDirectionKey is the attribute Key conforming to the + // "system.paging.direction" semantic conventions. It represents the paging + // access direction. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "in" + SystemPagingDirectionKey = attribute.Key("system.paging.direction") + + // SystemPagingFaultTypeKey is the attribute Key conforming to the + // "system.paging.fault.type" semantic conventions. It represents the paging + // fault type. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "minor" + SystemPagingFaultTypeKey = attribute.Key("system.paging.fault.type") + + // SystemPagingStateKey is the attribute Key conforming to the + // "system.paging.state" semantic conventions. It represents the memory paging + // state. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "free" + SystemPagingStateKey = attribute.Key("system.paging.state") +) + +// SystemDevice returns an attribute KeyValue conforming to the "system.device" +// semantic conventions. It represents the device identifier. +func SystemDevice(val string) attribute.KeyValue { + return SystemDeviceKey.String(val) +} + +// SystemFilesystemMode returns an attribute KeyValue conforming to the +// "system.filesystem.mode" semantic conventions. It represents the filesystem +// mode. +func SystemFilesystemMode(val string) attribute.KeyValue { + return SystemFilesystemModeKey.String(val) +} + +// SystemFilesystemMountpoint returns an attribute KeyValue conforming to the +// "system.filesystem.mountpoint" semantic conventions. It represents the +// filesystem mount path. +func SystemFilesystemMountpoint(val string) attribute.KeyValue { + return SystemFilesystemMountpointKey.String(val) +} + +// Enum values for system.filesystem.state +var ( + // used + // Stability: development + SystemFilesystemStateUsed = SystemFilesystemStateKey.String("used") + // free + // Stability: development + SystemFilesystemStateFree = SystemFilesystemStateKey.String("free") + // reserved + // Stability: development + SystemFilesystemStateReserved = SystemFilesystemStateKey.String("reserved") +) + +// Enum values for system.filesystem.type +var ( + // fat32 + // Stability: development + SystemFilesystemTypeFat32 = SystemFilesystemTypeKey.String("fat32") + // exfat + // Stability: development + SystemFilesystemTypeExfat = SystemFilesystemTypeKey.String("exfat") + // ntfs + // Stability: development + SystemFilesystemTypeNtfs = SystemFilesystemTypeKey.String("ntfs") + // refs + // Stability: development + SystemFilesystemTypeRefs = SystemFilesystemTypeKey.String("refs") + // hfsplus + // Stability: development + SystemFilesystemTypeHfsplus = SystemFilesystemTypeKey.String("hfsplus") + // ext4 + // Stability: development + SystemFilesystemTypeExt4 = SystemFilesystemTypeKey.String("ext4") +) + +// Enum values for system.memory.linux.slab.state +var ( + // reclaimable + // Stability: development + SystemMemoryLinuxSlabStateReclaimable = SystemMemoryLinuxSlabStateKey.String("reclaimable") + // unreclaimable + // Stability: development + SystemMemoryLinuxSlabStateUnreclaimable = SystemMemoryLinuxSlabStateKey.String("unreclaimable") +) + +// Enum values for system.memory.state +var ( + // Actual used virtual memory in bytes. + // Stability: development + SystemMemoryStateUsed = SystemMemoryStateKey.String("used") + // free + // Stability: development + SystemMemoryStateFree = SystemMemoryStateKey.String("free") + // buffers + // Stability: development + SystemMemoryStateBuffers = SystemMemoryStateKey.String("buffers") + // cached + // Stability: development + SystemMemoryStateCached = SystemMemoryStateKey.String("cached") +) + +// Enum values for system.paging.direction +var ( + // in + // Stability: development + SystemPagingDirectionIn = SystemPagingDirectionKey.String("in") + // out + // Stability: development + SystemPagingDirectionOut = SystemPagingDirectionKey.String("out") +) + +// Enum values for system.paging.fault.type +var ( + // major + // Stability: development + SystemPagingFaultTypeMajor = SystemPagingFaultTypeKey.String("major") + // minor + // Stability: development + SystemPagingFaultTypeMinor = SystemPagingFaultTypeKey.String("minor") +) + +// Enum values for system.paging.state +var ( + // used + // Stability: development + SystemPagingStateUsed = SystemPagingStateKey.String("used") + // free + // Stability: development + SystemPagingStateFree = SystemPagingStateKey.String("free") +) + +// Namespace: telemetry +const ( + // TelemetryDistroNameKey is the attribute Key conforming to the + // "telemetry.distro.name" semantic conventions. It represents the name of the + // auto instrumentation agent or distribution, if used. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "parts-unlimited-java" + // Note: Official auto instrumentation agents and distributions SHOULD set the + // `telemetry.distro.name` attribute to + // a string starting with `opentelemetry-`, e.g. + // `opentelemetry-java-instrumentation`. + TelemetryDistroNameKey = attribute.Key("telemetry.distro.name") + + // TelemetryDistroVersionKey is the attribute Key conforming to the + // "telemetry.distro.version" semantic conventions. It represents the version + // string of the auto instrumentation agent or distribution, if used. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "1.2.3" + TelemetryDistroVersionKey = attribute.Key("telemetry.distro.version") + + // TelemetrySDKLanguageKey is the attribute Key conforming to the + // "telemetry.sdk.language" semantic conventions. It represents the language of + // the telemetry SDK. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: + TelemetrySDKLanguageKey = attribute.Key("telemetry.sdk.language") + + // TelemetrySDKNameKey is the attribute Key conforming to the + // "telemetry.sdk.name" semantic conventions. It represents the name of the + // telemetry SDK as defined above. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "opentelemetry" + // Note: The OpenTelemetry SDK MUST set the `telemetry.sdk.name` attribute to + // `opentelemetry`. + // If another SDK, like a fork or a vendor-provided implementation, is used, + // this SDK MUST set the + // `telemetry.sdk.name` attribute to the fully-qualified class or module name of + // this SDK's main entry point + // or another suitable identifier depending on the language. + // The identifier `opentelemetry` is reserved and MUST NOT be used in this case. + // All custom identifiers SHOULD be stable across different versions of an + // implementation. + TelemetrySDKNameKey = attribute.Key("telemetry.sdk.name") + + // TelemetrySDKVersionKey is the attribute Key conforming to the + // "telemetry.sdk.version" semantic conventions. It represents the version + // string of the telemetry SDK. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "1.2.3" + TelemetrySDKVersionKey = attribute.Key("telemetry.sdk.version") +) + +// TelemetryDistroName returns an attribute KeyValue conforming to the +// "telemetry.distro.name" semantic conventions. It represents the name of the +// auto instrumentation agent or distribution, if used. +func TelemetryDistroName(val string) attribute.KeyValue { + return TelemetryDistroNameKey.String(val) +} + +// TelemetryDistroVersion returns an attribute KeyValue conforming to the +// "telemetry.distro.version" semantic conventions. It represents the version +// string of the auto instrumentation agent or distribution, if used. +func TelemetryDistroVersion(val string) attribute.KeyValue { + return TelemetryDistroVersionKey.String(val) +} + +// TelemetrySDKName returns an attribute KeyValue conforming to the +// "telemetry.sdk.name" semantic conventions. It represents the name of the +// telemetry SDK as defined above. +func TelemetrySDKName(val string) attribute.KeyValue { + return TelemetrySDKNameKey.String(val) +} + +// TelemetrySDKVersion returns an attribute KeyValue conforming to the +// "telemetry.sdk.version" semantic conventions. It represents the version string +// of the telemetry SDK. +func TelemetrySDKVersion(val string) attribute.KeyValue { + return TelemetrySDKVersionKey.String(val) +} + +// Enum values for telemetry.sdk.language +var ( + // cpp + // Stability: stable + TelemetrySDKLanguageCPP = TelemetrySDKLanguageKey.String("cpp") + // dotnet + // Stability: stable + TelemetrySDKLanguageDotnet = TelemetrySDKLanguageKey.String("dotnet") + // erlang + // Stability: stable + TelemetrySDKLanguageErlang = TelemetrySDKLanguageKey.String("erlang") + // go + // Stability: stable + TelemetrySDKLanguageGo = TelemetrySDKLanguageKey.String("go") + // java + // Stability: stable + TelemetrySDKLanguageJava = TelemetrySDKLanguageKey.String("java") + // nodejs + // Stability: stable + TelemetrySDKLanguageNodejs = TelemetrySDKLanguageKey.String("nodejs") + // php + // Stability: stable + TelemetrySDKLanguagePHP = TelemetrySDKLanguageKey.String("php") + // python + // Stability: stable + TelemetrySDKLanguagePython = TelemetrySDKLanguageKey.String("python") + // ruby + // Stability: stable + TelemetrySDKLanguageRuby = TelemetrySDKLanguageKey.String("ruby") + // rust + // Stability: stable + TelemetrySDKLanguageRust = TelemetrySDKLanguageKey.String("rust") + // swift + // Stability: stable + TelemetrySDKLanguageSwift = TelemetrySDKLanguageKey.String("swift") + // webjs + // Stability: stable + TelemetrySDKLanguageWebJS = TelemetrySDKLanguageKey.String("webjs") +) + +// Namespace: test +const ( + // TestCaseNameKey is the attribute Key conforming to the "test.case.name" + // semantic conventions. It represents the fully qualified human readable name + // of the [test case]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "org.example.TestCase1.test1", "example/tests/TestCase1.test1", + // "ExampleTestCase1_test1" + // + // [test case]: https://wikipedia.org/wiki/Test_case + TestCaseNameKey = attribute.Key("test.case.name") + + // TestCaseResultStatusKey is the attribute Key conforming to the + // "test.case.result.status" semantic conventions. It represents the status of + // the actual test case result from test execution. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "pass", "fail" + TestCaseResultStatusKey = attribute.Key("test.case.result.status") + + // TestSuiteNameKey is the attribute Key conforming to the "test.suite.name" + // semantic conventions. It represents the human readable name of a [test suite] + // . + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "TestSuite1" + // + // [test suite]: https://wikipedia.org/wiki/Test_suite + TestSuiteNameKey = attribute.Key("test.suite.name") + + // TestSuiteRunStatusKey is the attribute Key conforming to the + // "test.suite.run.status" semantic conventions. It represents the status of the + // test suite run. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "success", "failure", "skipped", "aborted", "timed_out", + // "in_progress" + TestSuiteRunStatusKey = attribute.Key("test.suite.run.status") +) + +// TestCaseName returns an attribute KeyValue conforming to the "test.case.name" +// semantic conventions. It represents the fully qualified human readable name of +// the [test case]. +// +// [test case]: https://wikipedia.org/wiki/Test_case +func TestCaseName(val string) attribute.KeyValue { + return TestCaseNameKey.String(val) +} + +// TestSuiteName returns an attribute KeyValue conforming to the +// "test.suite.name" semantic conventions. It represents the human readable name +// of a [test suite]. +// +// [test suite]: https://wikipedia.org/wiki/Test_suite +func TestSuiteName(val string) attribute.KeyValue { + return TestSuiteNameKey.String(val) +} + +// Enum values for test.case.result.status +var ( + // pass + // Stability: development + TestCaseResultStatusPass = TestCaseResultStatusKey.String("pass") + // fail + // Stability: development + TestCaseResultStatusFail = TestCaseResultStatusKey.String("fail") +) + +// Enum values for test.suite.run.status +var ( + // success + // Stability: development + TestSuiteRunStatusSuccess = TestSuiteRunStatusKey.String("success") + // failure + // Stability: development + TestSuiteRunStatusFailure = TestSuiteRunStatusKey.String("failure") + // skipped + // Stability: development + TestSuiteRunStatusSkipped = TestSuiteRunStatusKey.String("skipped") + // aborted + // Stability: development + TestSuiteRunStatusAborted = TestSuiteRunStatusKey.String("aborted") + // timed_out + // Stability: development + TestSuiteRunStatusTimedOut = TestSuiteRunStatusKey.String("timed_out") + // in_progress + // Stability: development + TestSuiteRunStatusInProgress = TestSuiteRunStatusKey.String("in_progress") +) + +// Namespace: thread +const ( + // ThreadIDKey is the attribute Key conforming to the "thread.id" semantic + // conventions. It represents the current "managed" thread ID (as opposed to OS + // thread ID). + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Note: + // Examples of where the value can be extracted from: + // + // | Language or platform | Source | + // | --- | --- | + // | JVM | `Thread.currentThread().threadId()` | + // | .NET | `Thread.CurrentThread.ManagedThreadId` | + // | Python | `threading.current_thread().ident` | + // | Ruby | `Thread.current.object_id` | + // | C++ | `std::this_thread::get_id()` | + // | Erlang | `erlang:self()` | + ThreadIDKey = attribute.Key("thread.id") + + // ThreadNameKey is the attribute Key conforming to the "thread.name" semantic + // conventions. It represents the current thread name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: main + // Note: + // Examples of where the value can be extracted from: + // + // | Language or platform | Source | + // | --- | --- | + // | JVM | `Thread.currentThread().getName()` | + // | .NET | `Thread.CurrentThread.Name` | + // | Python | `threading.current_thread().name` | + // | Ruby | `Thread.current.name` | + // | Erlang | `erlang:process_info(self(), registered_name)` | + ThreadNameKey = attribute.Key("thread.name") +) + +// ThreadID returns an attribute KeyValue conforming to the "thread.id" semantic +// conventions. It represents the current "managed" thread ID (as opposed to OS +// thread ID). +func ThreadID(val int) attribute.KeyValue { + return ThreadIDKey.Int(val) +} + +// ThreadName returns an attribute KeyValue conforming to the "thread.name" +// semantic conventions. It represents the current thread name. +func ThreadName(val string) attribute.KeyValue { + return ThreadNameKey.String(val) +} + +// Namespace: tls +const ( + // TLSCipherKey is the attribute Key conforming to the "tls.cipher" semantic + // conventions. It represents the string indicating the [cipher] used during the + // current connection. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + // "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" + // Note: The values allowed for `tls.cipher` MUST be one of the `Descriptions` + // of the [registered TLS Cipher Suits]. + // + // [cipher]: https://datatracker.ietf.org/doc/html/rfc5246#appendix-A.5 + // [registered TLS Cipher Suits]: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#table-tls-parameters-4 + TLSCipherKey = attribute.Key("tls.cipher") + + // TLSClientCertificateKey is the attribute Key conforming to the + // "tls.client.certificate" semantic conventions. It represents the PEM-encoded + // stand-alone certificate offered by the client. This is usually + // mutually-exclusive of `client.certificate_chain` since this value also exists + // in that list. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "MII..." + TLSClientCertificateKey = attribute.Key("tls.client.certificate") + + // TLSClientCertificateChainKey is the attribute Key conforming to the + // "tls.client.certificate_chain" semantic conventions. It represents the array + // of PEM-encoded certificates that make up the certificate chain offered by the + // client. This is usually mutually-exclusive of `client.certificate` since that + // value should be the first certificate in the chain. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "MII...", "MI..." + TLSClientCertificateChainKey = attribute.Key("tls.client.certificate_chain") + + // TLSClientHashMd5Key is the attribute Key conforming to the + // "tls.client.hash.md5" semantic conventions. It represents the certificate + // fingerprint using the MD5 digest of DER-encoded version of certificate + // offered by the client. For consistency with other hash values, this value + // should be formatted as an uppercase hash. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC" + TLSClientHashMd5Key = attribute.Key("tls.client.hash.md5") + + // TLSClientHashSha1Key is the attribute Key conforming to the + // "tls.client.hash.sha1" semantic conventions. It represents the certificate + // fingerprint using the SHA1 digest of DER-encoded version of certificate + // offered by the client. For consistency with other hash values, this value + // should be formatted as an uppercase hash. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "9E393D93138888D288266C2D915214D1D1CCEB2A" + TLSClientHashSha1Key = attribute.Key("tls.client.hash.sha1") + + // TLSClientHashSha256Key is the attribute Key conforming to the + // "tls.client.hash.sha256" semantic conventions. It represents the certificate + // fingerprint using the SHA256 digest of DER-encoded version of certificate + // offered by the client. For consistency with other hash values, this value + // should be formatted as an uppercase hash. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0" + TLSClientHashSha256Key = attribute.Key("tls.client.hash.sha256") + + // TLSClientIssuerKey is the attribute Key conforming to the "tls.client.issuer" + // semantic conventions. It represents the distinguished name of [subject] of + // the issuer of the x.509 certificate presented by the client. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "CN=Example Root CA, OU=Infrastructure Team, DC=example, DC=com" + // + // [subject]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6 + TLSClientIssuerKey = attribute.Key("tls.client.issuer") + + // TLSClientJa3Key is the attribute Key conforming to the "tls.client.ja3" + // semantic conventions. It represents a hash that identifies clients based on + // how they perform an SSL/TLS handshake. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "d4e5b18d6b55c71272893221c96ba240" + TLSClientJa3Key = attribute.Key("tls.client.ja3") + + // TLSClientNotAfterKey is the attribute Key conforming to the + // "tls.client.not_after" semantic conventions. It represents the date/Time + // indicating when client certificate is no longer considered valid. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2021-01-01T00:00:00.000Z" + TLSClientNotAfterKey = attribute.Key("tls.client.not_after") + + // TLSClientNotBeforeKey is the attribute Key conforming to the + // "tls.client.not_before" semantic conventions. It represents the date/Time + // indicating when client certificate is first considered valid. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "1970-01-01T00:00:00.000Z" + TLSClientNotBeforeKey = attribute.Key("tls.client.not_before") + + // TLSClientSubjectKey is the attribute Key conforming to the + // "tls.client.subject" semantic conventions. It represents the distinguished + // name of subject of the x.509 certificate presented by the client. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "CN=myclient, OU=Documentation Team, DC=example, DC=com" + TLSClientSubjectKey = attribute.Key("tls.client.subject") + + // TLSClientSupportedCiphersKey is the attribute Key conforming to the + // "tls.client.supported_ciphers" semantic conventions. It represents the array + // of ciphers offered by the client during the client hello. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + // "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" + TLSClientSupportedCiphersKey = attribute.Key("tls.client.supported_ciphers") + + // TLSCurveKey is the attribute Key conforming to the "tls.curve" semantic + // conventions. It represents the string indicating the curve used for the given + // cipher, when applicable. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "secp256r1" + TLSCurveKey = attribute.Key("tls.curve") + + // TLSEstablishedKey is the attribute Key conforming to the "tls.established" + // semantic conventions. It represents the boolean flag indicating if the TLS + // negotiation was successful and transitioned to an encrypted tunnel. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: true + TLSEstablishedKey = attribute.Key("tls.established") + + // TLSNextProtocolKey is the attribute Key conforming to the "tls.next_protocol" + // semantic conventions. It represents the string indicating the protocol being + // tunneled. Per the values in the [IANA registry], this string should be lower + // case. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "http/1.1" + // + // [IANA registry]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids + TLSNextProtocolKey = attribute.Key("tls.next_protocol") + + // TLSProtocolNameKey is the attribute Key conforming to the "tls.protocol.name" + // semantic conventions. It represents the normalized lowercase protocol name + // parsed from original string of the negotiated [SSL/TLS protocol version]. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // + // [SSL/TLS protocol version]: https://docs.openssl.org/1.1.1/man3/SSL_get_version/#return-values + TLSProtocolNameKey = attribute.Key("tls.protocol.name") + + // TLSProtocolVersionKey is the attribute Key conforming to the + // "tls.protocol.version" semantic conventions. It represents the numeric part + // of the version parsed from the original string of the negotiated + // [SSL/TLS protocol version]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "1.2", "3" + // + // [SSL/TLS protocol version]: https://docs.openssl.org/1.1.1/man3/SSL_get_version/#return-values + TLSProtocolVersionKey = attribute.Key("tls.protocol.version") + + // TLSResumedKey is the attribute Key conforming to the "tls.resumed" semantic + // conventions. It represents the boolean flag indicating if this TLS connection + // was resumed from an existing TLS negotiation. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: true + TLSResumedKey = attribute.Key("tls.resumed") + + // TLSServerCertificateKey is the attribute Key conforming to the + // "tls.server.certificate" semantic conventions. It represents the PEM-encoded + // stand-alone certificate offered by the server. This is usually + // mutually-exclusive of `server.certificate_chain` since this value also exists + // in that list. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "MII..." + TLSServerCertificateKey = attribute.Key("tls.server.certificate") + + // TLSServerCertificateChainKey is the attribute Key conforming to the + // "tls.server.certificate_chain" semantic conventions. It represents the array + // of PEM-encoded certificates that make up the certificate chain offered by the + // server. This is usually mutually-exclusive of `server.certificate` since that + // value should be the first certificate in the chain. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "MII...", "MI..." + TLSServerCertificateChainKey = attribute.Key("tls.server.certificate_chain") + + // TLSServerHashMd5Key is the attribute Key conforming to the + // "tls.server.hash.md5" semantic conventions. It represents the certificate + // fingerprint using the MD5 digest of DER-encoded version of certificate + // offered by the server. For consistency with other hash values, this value + // should be formatted as an uppercase hash. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC" + TLSServerHashMd5Key = attribute.Key("tls.server.hash.md5") + + // TLSServerHashSha1Key is the attribute Key conforming to the + // "tls.server.hash.sha1" semantic conventions. It represents the certificate + // fingerprint using the SHA1 digest of DER-encoded version of certificate + // offered by the server. For consistency with other hash values, this value + // should be formatted as an uppercase hash. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "9E393D93138888D288266C2D915214D1D1CCEB2A" + TLSServerHashSha1Key = attribute.Key("tls.server.hash.sha1") + + // TLSServerHashSha256Key is the attribute Key conforming to the + // "tls.server.hash.sha256" semantic conventions. It represents the certificate + // fingerprint using the SHA256 digest of DER-encoded version of certificate + // offered by the server. For consistency with other hash values, this value + // should be formatted as an uppercase hash. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0" + TLSServerHashSha256Key = attribute.Key("tls.server.hash.sha256") + + // TLSServerIssuerKey is the attribute Key conforming to the "tls.server.issuer" + // semantic conventions. It represents the distinguished name of [subject] of + // the issuer of the x.509 certificate presented by the client. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "CN=Example Root CA, OU=Infrastructure Team, DC=example, DC=com" + // + // [subject]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6 + TLSServerIssuerKey = attribute.Key("tls.server.issuer") + + // TLSServerJa3sKey is the attribute Key conforming to the "tls.server.ja3s" + // semantic conventions. It represents a hash that identifies servers based on + // how they perform an SSL/TLS handshake. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "d4e5b18d6b55c71272893221c96ba240" + TLSServerJa3sKey = attribute.Key("tls.server.ja3s") + + // TLSServerNotAfterKey is the attribute Key conforming to the + // "tls.server.not_after" semantic conventions. It represents the date/Time + // indicating when server certificate is no longer considered valid. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "2021-01-01T00:00:00.000Z" + TLSServerNotAfterKey = attribute.Key("tls.server.not_after") + + // TLSServerNotBeforeKey is the attribute Key conforming to the + // "tls.server.not_before" semantic conventions. It represents the date/Time + // indicating when server certificate is first considered valid. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "1970-01-01T00:00:00.000Z" + TLSServerNotBeforeKey = attribute.Key("tls.server.not_before") + + // TLSServerSubjectKey is the attribute Key conforming to the + // "tls.server.subject" semantic conventions. It represents the distinguished + // name of subject of the x.509 certificate presented by the server. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "CN=myserver, OU=Documentation Team, DC=example, DC=com" + TLSServerSubjectKey = attribute.Key("tls.server.subject") +) + +// TLSCipher returns an attribute KeyValue conforming to the "tls.cipher" +// semantic conventions. It represents the string indicating the [cipher] used +// during the current connection. +// +// [cipher]: https://datatracker.ietf.org/doc/html/rfc5246#appendix-A.5 +func TLSCipher(val string) attribute.KeyValue { + return TLSCipherKey.String(val) +} + +// TLSClientCertificate returns an attribute KeyValue conforming to the +// "tls.client.certificate" semantic conventions. It represents the PEM-encoded +// stand-alone certificate offered by the client. This is usually +// mutually-exclusive of `client.certificate_chain` since this value also exists +// in that list. +func TLSClientCertificate(val string) attribute.KeyValue { + return TLSClientCertificateKey.String(val) +} + +// TLSClientCertificateChain returns an attribute KeyValue conforming to the +// "tls.client.certificate_chain" semantic conventions. It represents the array +// of PEM-encoded certificates that make up the certificate chain offered by the +// client. This is usually mutually-exclusive of `client.certificate` since that +// value should be the first certificate in the chain. +func TLSClientCertificateChain(val ...string) attribute.KeyValue { + return TLSClientCertificateChainKey.StringSlice(val) +} + +// TLSClientHashMd5 returns an attribute KeyValue conforming to the +// "tls.client.hash.md5" semantic conventions. It represents the certificate +// fingerprint using the MD5 digest of DER-encoded version of certificate offered +// by the client. For consistency with other hash values, this value should be +// formatted as an uppercase hash. +func TLSClientHashMd5(val string) attribute.KeyValue { + return TLSClientHashMd5Key.String(val) +} + +// TLSClientHashSha1 returns an attribute KeyValue conforming to the +// "tls.client.hash.sha1" semantic conventions. It represents the certificate +// fingerprint using the SHA1 digest of DER-encoded version of certificate +// offered by the client. For consistency with other hash values, this value +// should be formatted as an uppercase hash. +func TLSClientHashSha1(val string) attribute.KeyValue { + return TLSClientHashSha1Key.String(val) +} + +// TLSClientHashSha256 returns an attribute KeyValue conforming to the +// "tls.client.hash.sha256" semantic conventions. It represents the certificate +// fingerprint using the SHA256 digest of DER-encoded version of certificate +// offered by the client. For consistency with other hash values, this value +// should be formatted as an uppercase hash. +func TLSClientHashSha256(val string) attribute.KeyValue { + return TLSClientHashSha256Key.String(val) +} + +// TLSClientIssuer returns an attribute KeyValue conforming to the +// "tls.client.issuer" semantic conventions. It represents the distinguished name +// of [subject] of the issuer of the x.509 certificate presented by the client. +// +// [subject]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6 +func TLSClientIssuer(val string) attribute.KeyValue { + return TLSClientIssuerKey.String(val) +} + +// TLSClientJa3 returns an attribute KeyValue conforming to the "tls.client.ja3" +// semantic conventions. It represents a hash that identifies clients based on +// how they perform an SSL/TLS handshake. +func TLSClientJa3(val string) attribute.KeyValue { + return TLSClientJa3Key.String(val) +} + +// TLSClientNotAfter returns an attribute KeyValue conforming to the +// "tls.client.not_after" semantic conventions. It represents the date/Time +// indicating when client certificate is no longer considered valid. +func TLSClientNotAfter(val string) attribute.KeyValue { + return TLSClientNotAfterKey.String(val) +} + +// TLSClientNotBefore returns an attribute KeyValue conforming to the +// "tls.client.not_before" semantic conventions. It represents the date/Time +// indicating when client certificate is first considered valid. +func TLSClientNotBefore(val string) attribute.KeyValue { + return TLSClientNotBeforeKey.String(val) +} + +// TLSClientSubject returns an attribute KeyValue conforming to the +// "tls.client.subject" semantic conventions. It represents the distinguished +// name of subject of the x.509 certificate presented by the client. +func TLSClientSubject(val string) attribute.KeyValue { + return TLSClientSubjectKey.String(val) +} + +// TLSClientSupportedCiphers returns an attribute KeyValue conforming to the +// "tls.client.supported_ciphers" semantic conventions. It represents the array +// of ciphers offered by the client during the client hello. +func TLSClientSupportedCiphers(val ...string) attribute.KeyValue { + return TLSClientSupportedCiphersKey.StringSlice(val) +} + +// TLSCurve returns an attribute KeyValue conforming to the "tls.curve" semantic +// conventions. It represents the string indicating the curve used for the given +// cipher, when applicable. +func TLSCurve(val string) attribute.KeyValue { + return TLSCurveKey.String(val) +} + +// TLSEstablished returns an attribute KeyValue conforming to the +// "tls.established" semantic conventions. It represents the boolean flag +// indicating if the TLS negotiation was successful and transitioned to an +// encrypted tunnel. +func TLSEstablished(val bool) attribute.KeyValue { + return TLSEstablishedKey.Bool(val) +} + +// TLSNextProtocol returns an attribute KeyValue conforming to the +// "tls.next_protocol" semantic conventions. It represents the string indicating +// the protocol being tunneled. Per the values in the [IANA registry], this +// string should be lower case. +// +// [IANA registry]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids +func TLSNextProtocol(val string) attribute.KeyValue { + return TLSNextProtocolKey.String(val) +} + +// TLSProtocolVersion returns an attribute KeyValue conforming to the +// "tls.protocol.version" semantic conventions. It represents the numeric part of +// the version parsed from the original string of the negotiated +// [SSL/TLS protocol version]. +// +// [SSL/TLS protocol version]: https://docs.openssl.org/1.1.1/man3/SSL_get_version/#return-values +func TLSProtocolVersion(val string) attribute.KeyValue { + return TLSProtocolVersionKey.String(val) +} + +// TLSResumed returns an attribute KeyValue conforming to the "tls.resumed" +// semantic conventions. It represents the boolean flag indicating if this TLS +// connection was resumed from an existing TLS negotiation. +func TLSResumed(val bool) attribute.KeyValue { + return TLSResumedKey.Bool(val) +} + +// TLSServerCertificate returns an attribute KeyValue conforming to the +// "tls.server.certificate" semantic conventions. It represents the PEM-encoded +// stand-alone certificate offered by the server. This is usually +// mutually-exclusive of `server.certificate_chain` since this value also exists +// in that list. +func TLSServerCertificate(val string) attribute.KeyValue { + return TLSServerCertificateKey.String(val) +} + +// TLSServerCertificateChain returns an attribute KeyValue conforming to the +// "tls.server.certificate_chain" semantic conventions. It represents the array +// of PEM-encoded certificates that make up the certificate chain offered by the +// server. This is usually mutually-exclusive of `server.certificate` since that +// value should be the first certificate in the chain. +func TLSServerCertificateChain(val ...string) attribute.KeyValue { + return TLSServerCertificateChainKey.StringSlice(val) +} + +// TLSServerHashMd5 returns an attribute KeyValue conforming to the +// "tls.server.hash.md5" semantic conventions. It represents the certificate +// fingerprint using the MD5 digest of DER-encoded version of certificate offered +// by the server. For consistency with other hash values, this value should be +// formatted as an uppercase hash. +func TLSServerHashMd5(val string) attribute.KeyValue { + return TLSServerHashMd5Key.String(val) +} + +// TLSServerHashSha1 returns an attribute KeyValue conforming to the +// "tls.server.hash.sha1" semantic conventions. It represents the certificate +// fingerprint using the SHA1 digest of DER-encoded version of certificate +// offered by the server. For consistency with other hash values, this value +// should be formatted as an uppercase hash. +func TLSServerHashSha1(val string) attribute.KeyValue { + return TLSServerHashSha1Key.String(val) +} + +// TLSServerHashSha256 returns an attribute KeyValue conforming to the +// "tls.server.hash.sha256" semantic conventions. It represents the certificate +// fingerprint using the SHA256 digest of DER-encoded version of certificate +// offered by the server. For consistency with other hash values, this value +// should be formatted as an uppercase hash. +func TLSServerHashSha256(val string) attribute.KeyValue { + return TLSServerHashSha256Key.String(val) +} + +// TLSServerIssuer returns an attribute KeyValue conforming to the +// "tls.server.issuer" semantic conventions. It represents the distinguished name +// of [subject] of the issuer of the x.509 certificate presented by the client. +// +// [subject]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6 +func TLSServerIssuer(val string) attribute.KeyValue { + return TLSServerIssuerKey.String(val) +} + +// TLSServerJa3s returns an attribute KeyValue conforming to the +// "tls.server.ja3s" semantic conventions. It represents a hash that identifies +// servers based on how they perform an SSL/TLS handshake. +func TLSServerJa3s(val string) attribute.KeyValue { + return TLSServerJa3sKey.String(val) +} + +// TLSServerNotAfter returns an attribute KeyValue conforming to the +// "tls.server.not_after" semantic conventions. It represents the date/Time +// indicating when server certificate is no longer considered valid. +func TLSServerNotAfter(val string) attribute.KeyValue { + return TLSServerNotAfterKey.String(val) +} + +// TLSServerNotBefore returns an attribute KeyValue conforming to the +// "tls.server.not_before" semantic conventions. It represents the date/Time +// indicating when server certificate is first considered valid. +func TLSServerNotBefore(val string) attribute.KeyValue { + return TLSServerNotBeforeKey.String(val) +} + +// TLSServerSubject returns an attribute KeyValue conforming to the +// "tls.server.subject" semantic conventions. It represents the distinguished +// name of subject of the x.509 certificate presented by the server. +func TLSServerSubject(val string) attribute.KeyValue { + return TLSServerSubjectKey.String(val) +} + +// Enum values for tls.protocol.name +var ( + // ssl + // Stability: development + TLSProtocolNameSsl = TLSProtocolNameKey.String("ssl") + // tls + // Stability: development + TLSProtocolNameTLS = TLSProtocolNameKey.String("tls") +) + +// Namespace: url +const ( + // URLDomainKey is the attribute Key conforming to the "url.domain" semantic + // conventions. It represents the domain extracted from the `url.full`, such as + // "opentelemetry.io". + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "www.foo.bar", "opentelemetry.io", "3.12.167.2", + // "[1080:0:0:0:8:800:200C:417A]" + // Note: In some cases a URL may refer to an IP and/or port directly, without a + // domain name. In this case, the IP address would go to the domain field. If + // the URL contains a [literal IPv6 address] enclosed by `[` and `]`, the `[` + // and `]` characters should also be captured in the domain field. + // + // [literal IPv6 address]: https://www.rfc-editor.org/rfc/rfc2732#section-2 + URLDomainKey = attribute.Key("url.domain") + + // URLExtensionKey is the attribute Key conforming to the "url.extension" + // semantic conventions. It represents the file extension extracted from the + // `url.full`, excluding the leading dot. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "png", "gz" + // Note: The file extension is only set if it exists, as not every url has a + // file extension. When the file name has multiple extensions `example.tar.gz`, + // only the last one should be captured `gz`, not `tar.gz`. + URLExtensionKey = attribute.Key("url.extension") + + // URLFragmentKey is the attribute Key conforming to the "url.fragment" semantic + // conventions. It represents the [URI fragment] component. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "SemConv" + // + // [URI fragment]: https://www.rfc-editor.org/rfc/rfc3986#section-3.5 + URLFragmentKey = attribute.Key("url.fragment") + + // URLFullKey is the attribute Key conforming to the "url.full" semantic + // conventions. It represents the absolute URL describing a network resource + // according to [RFC3986]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "https://www.foo.bar/search?q=OpenTelemetry#SemConv", "//localhost" + // Note: For network calls, URL usually has + // `scheme://host[:port][path][?query][#fragment]` format, where the fragment + // is not transmitted over HTTP, but if it is known, it SHOULD be included + // nevertheless. + // + // `url.full` MUST NOT contain credentials passed via URL in form of + // `https://username:password@www.example.com/`. + // In such case username and password SHOULD be redacted and attribute's value + // SHOULD be `https://REDACTED:REDACTED@www.example.com/`. + // + // `url.full` SHOULD capture the absolute URL when it is available (or can be + // reconstructed). + // + // Sensitive content provided in `url.full` SHOULD be scrubbed when + // instrumentations can identify it. + // + // + // Query string values for the following keys SHOULD be redacted by default and + // replaced by the + // value `REDACTED`: + // + // - [`AWSAccessKeyId`] + // - [`Signature`] + // - [`sig`] + // - [`X-Goog-Signature`] + // + // This list is subject to change over time. + // + // When a query string value is redacted, the query string key SHOULD still be + // preserved, e.g. + // `https://www.example.com/path?color=blue&sig=REDACTED`. + // + // [RFC3986]: https://www.rfc-editor.org/rfc/rfc3986 + // [`AWSAccessKeyId`]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/RESTAuthentication.html#RESTAuthenticationQueryStringAuth + // [`Signature`]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/RESTAuthentication.html#RESTAuthenticationQueryStringAuth + // [`sig`]: https://learn.microsoft.com/azure/storage/common/storage-sas-overview#sas-token + // [`X-Goog-Signature`]: https://cloud.google.com/storage/docs/access-control/signed-urls + URLFullKey = attribute.Key("url.full") + + // URLOriginalKey is the attribute Key conforming to the "url.original" semantic + // conventions. It represents the unmodified original URL as seen in the event + // source. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "https://www.foo.bar/search?q=OpenTelemetry#SemConv", + // "search?q=OpenTelemetry" + // Note: In network monitoring, the observed URL may be a full URL, whereas in + // access logs, the URL is often just represented as a path. This field is meant + // to represent the URL as it was observed, complete or not. + // `url.original` might contain credentials passed via URL in form of + // `https://username:password@www.example.com/`. In such case password and + // username SHOULD NOT be redacted and attribute's value SHOULD remain the same. + URLOriginalKey = attribute.Key("url.original") + + // URLPathKey is the attribute Key conforming to the "url.path" semantic + // conventions. It represents the [URI path] component. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "/search" + // Note: Sensitive content provided in `url.path` SHOULD be scrubbed when + // instrumentations can identify it. + // + // [URI path]: https://www.rfc-editor.org/rfc/rfc3986#section-3.3 + URLPathKey = attribute.Key("url.path") + + // URLPortKey is the attribute Key conforming to the "url.port" semantic + // conventions. It represents the port extracted from the `url.full`. + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 443 + URLPortKey = attribute.Key("url.port") + + // URLQueryKey is the attribute Key conforming to the "url.query" semantic + // conventions. It represents the [URI query] component. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "q=OpenTelemetry" + // Note: Sensitive content provided in `url.query` SHOULD be scrubbed when + // instrumentations can identify it. + // + // + // Query string values for the following keys SHOULD be redacted by default and + // replaced by the value `REDACTED`: + // + // - [`AWSAccessKeyId`] + // - [`Signature`] + // - [`sig`] + // - [`X-Goog-Signature`] + // + // This list is subject to change over time. + // + // When a query string value is redacted, the query string key SHOULD still be + // preserved, e.g. + // `q=OpenTelemetry&sig=REDACTED`. + // + // [URI query]: https://www.rfc-editor.org/rfc/rfc3986#section-3.4 + // [`AWSAccessKeyId`]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/RESTAuthentication.html#RESTAuthenticationQueryStringAuth + // [`Signature`]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/RESTAuthentication.html#RESTAuthenticationQueryStringAuth + // [`sig`]: https://learn.microsoft.com/azure/storage/common/storage-sas-overview#sas-token + // [`X-Goog-Signature`]: https://cloud.google.com/storage/docs/access-control/signed-urls + URLQueryKey = attribute.Key("url.query") + + // URLRegisteredDomainKey is the attribute Key conforming to the + // "url.registered_domain" semantic conventions. It represents the highest + // registered url domain, stripped of the subdomain. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "example.com", "foo.co.uk" + // Note: This value can be determined precisely with the [public suffix list]. + // For example, the registered domain for `foo.example.com` is `example.com`. + // Trying to approximate this by simply taking the last two labels will not work + // well for TLDs such as `co.uk`. + // + // [public suffix list]: https://publicsuffix.org/ + URLRegisteredDomainKey = attribute.Key("url.registered_domain") + + // URLSchemeKey is the attribute Key conforming to the "url.scheme" semantic + // conventions. It represents the [URI scheme] component identifying the used + // protocol. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "https", "ftp", "telnet" + // + // [URI scheme]: https://www.rfc-editor.org/rfc/rfc3986#section-3.1 + URLSchemeKey = attribute.Key("url.scheme") + + // URLSubdomainKey is the attribute Key conforming to the "url.subdomain" + // semantic conventions. It represents the subdomain portion of a fully + // qualified domain name includes all of the names except the host name under + // the registered_domain. In a partially qualified domain, or if the + // qualification level of the full name cannot be determined, subdomain contains + // all of the names below the registered domain. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "east", "sub2.sub1" + // Note: The subdomain portion of `www.east.mydomain.co.uk` is `east`. If the + // domain has multiple levels of subdomain, such as `sub2.sub1.example.com`, the + // subdomain field should contain `sub2.sub1`, with no trailing period. + URLSubdomainKey = attribute.Key("url.subdomain") + + // URLTemplateKey is the attribute Key conforming to the "url.template" semantic + // conventions. It represents the low-cardinality template of an + // [absolute path reference]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "/users/{id}", "/users/:id", "/users?id={id}" + // + // [absolute path reference]: https://www.rfc-editor.org/rfc/rfc3986#section-4.2 + URLTemplateKey = attribute.Key("url.template") + + // URLTopLevelDomainKey is the attribute Key conforming to the + // "url.top_level_domain" semantic conventions. It represents the effective top + // level domain (eTLD), also known as the domain suffix, is the last part of the + // domain name. For example, the top level domain for example.com is `com`. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "com", "co.uk" + // Note: This value can be determined precisely with the [public suffix list]. + // + // [public suffix list]: https://publicsuffix.org/ + URLTopLevelDomainKey = attribute.Key("url.top_level_domain") +) + +// URLDomain returns an attribute KeyValue conforming to the "url.domain" +// semantic conventions. It represents the domain extracted from the `url.full`, +// such as "opentelemetry.io". +func URLDomain(val string) attribute.KeyValue { + return URLDomainKey.String(val) +} + +// URLExtension returns an attribute KeyValue conforming to the "url.extension" +// semantic conventions. It represents the file extension extracted from the +// `url.full`, excluding the leading dot. +func URLExtension(val string) attribute.KeyValue { + return URLExtensionKey.String(val) +} + +// URLFragment returns an attribute KeyValue conforming to the "url.fragment" +// semantic conventions. It represents the [URI fragment] component. +// +// [URI fragment]: https://www.rfc-editor.org/rfc/rfc3986#section-3.5 +func URLFragment(val string) attribute.KeyValue { + return URLFragmentKey.String(val) +} + +// URLFull returns an attribute KeyValue conforming to the "url.full" semantic +// conventions. It represents the absolute URL describing a network resource +// according to [RFC3986]. +// +// [RFC3986]: https://www.rfc-editor.org/rfc/rfc3986 +func URLFull(val string) attribute.KeyValue { + return URLFullKey.String(val) +} + +// URLOriginal returns an attribute KeyValue conforming to the "url.original" +// semantic conventions. It represents the unmodified original URL as seen in the +// event source. +func URLOriginal(val string) attribute.KeyValue { + return URLOriginalKey.String(val) +} + +// URLPath returns an attribute KeyValue conforming to the "url.path" semantic +// conventions. It represents the [URI path] component. +// +// [URI path]: https://www.rfc-editor.org/rfc/rfc3986#section-3.3 +func URLPath(val string) attribute.KeyValue { + return URLPathKey.String(val) +} + +// URLPort returns an attribute KeyValue conforming to the "url.port" semantic +// conventions. It represents the port extracted from the `url.full`. +func URLPort(val int) attribute.KeyValue { + return URLPortKey.Int(val) +} + +// URLQuery returns an attribute KeyValue conforming to the "url.query" semantic +// conventions. It represents the [URI query] component. +// +// [URI query]: https://www.rfc-editor.org/rfc/rfc3986#section-3.4 +func URLQuery(val string) attribute.KeyValue { + return URLQueryKey.String(val) +} + +// URLRegisteredDomain returns an attribute KeyValue conforming to the +// "url.registered_domain" semantic conventions. It represents the highest +// registered url domain, stripped of the subdomain. +func URLRegisteredDomain(val string) attribute.KeyValue { + return URLRegisteredDomainKey.String(val) +} + +// URLScheme returns an attribute KeyValue conforming to the "url.scheme" +// semantic conventions. It represents the [URI scheme] component identifying the +// used protocol. +// +// [URI scheme]: https://www.rfc-editor.org/rfc/rfc3986#section-3.1 +func URLScheme(val string) attribute.KeyValue { + return URLSchemeKey.String(val) +} + +// URLSubdomain returns an attribute KeyValue conforming to the "url.subdomain" +// semantic conventions. It represents the subdomain portion of a fully qualified +// domain name includes all of the names except the host name under the +// registered_domain. In a partially qualified domain, or if the qualification +// level of the full name cannot be determined, subdomain contains all of the +// names below the registered domain. +func URLSubdomain(val string) attribute.KeyValue { + return URLSubdomainKey.String(val) +} + +// URLTemplate returns an attribute KeyValue conforming to the "url.template" +// semantic conventions. It represents the low-cardinality template of an +// [absolute path reference]. +// +// [absolute path reference]: https://www.rfc-editor.org/rfc/rfc3986#section-4.2 +func URLTemplate(val string) attribute.KeyValue { + return URLTemplateKey.String(val) +} + +// URLTopLevelDomain returns an attribute KeyValue conforming to the +// "url.top_level_domain" semantic conventions. It represents the effective top +// level domain (eTLD), also known as the domain suffix, is the last part of the +// domain name. For example, the top level domain for example.com is `com`. +func URLTopLevelDomain(val string) attribute.KeyValue { + return URLTopLevelDomainKey.String(val) +} + +// Namespace: user +const ( + // UserEmailKey is the attribute Key conforming to the "user.email" semantic + // conventions. It represents the user email address. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "a.einstein@example.com" + UserEmailKey = attribute.Key("user.email") + + // UserFullNameKey is the attribute Key conforming to the "user.full_name" + // semantic conventions. It represents the user's full name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Albert Einstein" + UserFullNameKey = attribute.Key("user.full_name") + + // UserHashKey is the attribute Key conforming to the "user.hash" semantic + // conventions. It represents the unique user hash to correlate information for + // a user in anonymized form. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "364fc68eaf4c8acec74a4e52d7d1feaa" + // Note: Useful if `user.id` or `user.name` contain confidential information and + // cannot be used. + UserHashKey = attribute.Key("user.hash") + + // UserIDKey is the attribute Key conforming to the "user.id" semantic + // conventions. It represents the unique identifier of the user. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "S-1-5-21-202424912787-2692429404-2351956786-1000" + UserIDKey = attribute.Key("user.id") + + // UserNameKey is the attribute Key conforming to the "user.name" semantic + // conventions. It represents the short name or login/username of the user. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "a.einstein" + UserNameKey = attribute.Key("user.name") + + // UserRolesKey is the attribute Key conforming to the "user.roles" semantic + // conventions. It represents the array of user roles at the time of the event. + // + // Type: string[] + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "admin", "reporting_user" + UserRolesKey = attribute.Key("user.roles") +) + +// UserEmail returns an attribute KeyValue conforming to the "user.email" +// semantic conventions. It represents the user email address. +func UserEmail(val string) attribute.KeyValue { + return UserEmailKey.String(val) +} + +// UserFullName returns an attribute KeyValue conforming to the "user.full_name" +// semantic conventions. It represents the user's full name. +func UserFullName(val string) attribute.KeyValue { + return UserFullNameKey.String(val) +} + +// UserHash returns an attribute KeyValue conforming to the "user.hash" semantic +// conventions. It represents the unique user hash to correlate information for a +// user in anonymized form. +func UserHash(val string) attribute.KeyValue { + return UserHashKey.String(val) +} + +// UserID returns an attribute KeyValue conforming to the "user.id" semantic +// conventions. It represents the unique identifier of the user. +func UserID(val string) attribute.KeyValue { + return UserIDKey.String(val) +} + +// UserName returns an attribute KeyValue conforming to the "user.name" semantic +// conventions. It represents the short name or login/username of the user. +func UserName(val string) attribute.KeyValue { + return UserNameKey.String(val) +} + +// UserRoles returns an attribute KeyValue conforming to the "user.roles" +// semantic conventions. It represents the array of user roles at the time of the +// event. +func UserRoles(val ...string) attribute.KeyValue { + return UserRolesKey.StringSlice(val) +} + +// Namespace: user_agent +const ( + // UserAgentNameKey is the attribute Key conforming to the "user_agent.name" + // semantic conventions. It represents the name of the user-agent extracted from + // original. Usually refers to the browser's name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Safari", "YourApp" + // Note: [Example] of extracting browser's name from original string. In the + // case of using a user-agent for non-browser products, such as microservices + // with multiple names/versions inside the `user_agent.original`, the most + // significant name SHOULD be selected. In such a scenario it should align with + // `user_agent.version` + // + // [Example]: https://uaparser.dev/#demo + UserAgentNameKey = attribute.Key("user_agent.name") + + // UserAgentOriginalKey is the attribute Key conforming to the + // "user_agent.original" semantic conventions. It represents the value of the + // [HTTP User-Agent] header sent by the client. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Stable + // + // Examples: "CERN-LineMode/2.15 libwww/2.17b3", "Mozilla/5.0 (iPhone; CPU + // iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) + // Version/14.1.2 Mobile/15E148 Safari/604.1", "YourApp/1.0.0 + // grpc-java-okhttp/1.27.2" + // + // [HTTP User-Agent]: https://www.rfc-editor.org/rfc/rfc9110.html#field.user-agent + UserAgentOriginalKey = attribute.Key("user_agent.original") + + // UserAgentOSNameKey is the attribute Key conforming to the + // "user_agent.os.name" semantic conventions. It represents the human readable + // operating system name. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "iOS", "Android", "Ubuntu" + // Note: For mapping user agent strings to OS names, libraries such as + // [ua-parser] can be utilized. + // + // [ua-parser]: https://github.com/ua-parser + UserAgentOSNameKey = attribute.Key("user_agent.os.name") + + // UserAgentOSVersionKey is the attribute Key conforming to the + // "user_agent.os.version" semantic conventions. It represents the version + // string of the operating system as defined in [Version Attributes]. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "14.2.1", "18.04.1" + // Note: For mapping user agent strings to OS versions, libraries such as + // [ua-parser] can be utilized. + // + // [Version Attributes]: /docs/resource/README.md#version-attributes + // [ua-parser]: https://github.com/ua-parser + UserAgentOSVersionKey = attribute.Key("user_agent.os.version") + + // UserAgentSyntheticTypeKey is the attribute Key conforming to the + // "user_agent.synthetic.type" semantic conventions. It represents the specifies + // the category of synthetic traffic, such as tests or bots. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // Note: This attribute MAY be derived from the contents of the + // `user_agent.original` attribute. Components that populate the attribute are + // responsible for determining what they consider to be synthetic bot or test + // traffic. This attribute can either be set for self-identification purposes, + // or on telemetry detected to be generated as a result of a synthetic request. + // This attribute is useful for distinguishing between genuine client traffic + // and synthetic traffic generated by bots or tests. + UserAgentSyntheticTypeKey = attribute.Key("user_agent.synthetic.type") + + // UserAgentVersionKey is the attribute Key conforming to the + // "user_agent.version" semantic conventions. It represents the version of the + // user-agent extracted from original. Usually refers to the browser's version. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "14.1.2", "1.0.0" + // Note: [Example] of extracting browser's version from original string. In the + // case of using a user-agent for non-browser products, such as microservices + // with multiple names/versions inside the `user_agent.original`, the most + // significant version SHOULD be selected. In such a scenario it should align + // with `user_agent.name` + // + // [Example]: https://uaparser.dev/#demo + UserAgentVersionKey = attribute.Key("user_agent.version") +) + +// UserAgentName returns an attribute KeyValue conforming to the +// "user_agent.name" semantic conventions. It represents the name of the +// user-agent extracted from original. Usually refers to the browser's name. +func UserAgentName(val string) attribute.KeyValue { + return UserAgentNameKey.String(val) +} + +// UserAgentOriginal returns an attribute KeyValue conforming to the +// "user_agent.original" semantic conventions. It represents the value of the +// [HTTP User-Agent] header sent by the client. +// +// [HTTP User-Agent]: https://www.rfc-editor.org/rfc/rfc9110.html#field.user-agent +func UserAgentOriginal(val string) attribute.KeyValue { + return UserAgentOriginalKey.String(val) +} + +// UserAgentOSName returns an attribute KeyValue conforming to the +// "user_agent.os.name" semantic conventions. It represents the human readable +// operating system name. +func UserAgentOSName(val string) attribute.KeyValue { + return UserAgentOSNameKey.String(val) +} + +// UserAgentOSVersion returns an attribute KeyValue conforming to the +// "user_agent.os.version" semantic conventions. It represents the version string +// of the operating system as defined in [Version Attributes]. +// +// [Version Attributes]: /docs/resource/README.md#version-attributes +func UserAgentOSVersion(val string) attribute.KeyValue { + return UserAgentOSVersionKey.String(val) +} + +// UserAgentVersion returns an attribute KeyValue conforming to the +// "user_agent.version" semantic conventions. It represents the version of the +// user-agent extracted from original. Usually refers to the browser's version. +func UserAgentVersion(val string) attribute.KeyValue { + return UserAgentVersionKey.String(val) +} + +// Enum values for user_agent.synthetic.type +var ( + // Bot source. + // Stability: development + UserAgentSyntheticTypeBot = UserAgentSyntheticTypeKey.String("bot") + // Synthetic test source. + // Stability: development + UserAgentSyntheticTypeTest = UserAgentSyntheticTypeKey.String("test") +) + +// Namespace: vcs +const ( + // VCSChangeIDKey is the attribute Key conforming to the "vcs.change.id" + // semantic conventions. It represents the ID of the change (pull request/merge + // request/changelist) if applicable. This is usually a unique (within + // repository) identifier generated by the VCS system. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "123" + VCSChangeIDKey = attribute.Key("vcs.change.id") + + // VCSChangeStateKey is the attribute Key conforming to the "vcs.change.state" + // semantic conventions. It represents the state of the change (pull + // request/merge request/changelist). + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "open", "closed", "merged" + VCSChangeStateKey = attribute.Key("vcs.change.state") + + // VCSChangeTitleKey is the attribute Key conforming to the "vcs.change.title" + // semantic conventions. It represents the human readable title of the change + // (pull request/merge request/changelist). This title is often a brief summary + // of the change and may get merged in to a ref as the commit summary. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Fixes broken thing", "feat: add my new feature", "[chore] update + // dependency" + VCSChangeTitleKey = attribute.Key("vcs.change.title") + + // VCSLineChangeTypeKey is the attribute Key conforming to the + // "vcs.line_change.type" semantic conventions. It represents the type of line + // change being measured on a branch or change. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "added", "removed" + VCSLineChangeTypeKey = attribute.Key("vcs.line_change.type") + + // VCSOwnerNameKey is the attribute Key conforming to the "vcs.owner.name" + // semantic conventions. It represents the group owner within the version + // control system. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-org", "myteam", "business-unit" + VCSOwnerNameKey = attribute.Key("vcs.owner.name") + + // VCSProviderNameKey is the attribute Key conforming to the "vcs.provider.name" + // semantic conventions. It represents the name of the version control system + // provider. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "github", "gitlab", "gitea", "bitbucket" + VCSProviderNameKey = attribute.Key("vcs.provider.name") + + // VCSRefBaseNameKey is the attribute Key conforming to the "vcs.ref.base.name" + // semantic conventions. It represents the name of the [reference] such as + // **branch** or **tag** in the repository. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-feature-branch", "tag-1-test" + // Note: `base` refers to the starting point of a change. For example, `main` + // would be the base reference of type branch if you've created a new + // reference of type branch from it and created new commits. + // + // [reference]: https://git-scm.com/docs/gitglossary#def_ref + VCSRefBaseNameKey = attribute.Key("vcs.ref.base.name") + + // VCSRefBaseRevisionKey is the attribute Key conforming to the + // "vcs.ref.base.revision" semantic conventions. It represents the revision, + // literally [revised version], The revision most often refers to a commit + // object in Git, or a revision number in SVN. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "9d59409acf479dfa0df1aa568182e43e43df8bbe28d60fcf2bc52e30068802cc", + // "main", "123", "HEAD" + // Note: `base` refers to the starting point of a change. For example, `main` + // would be the base reference of type branch if you've created a new + // reference of type branch from it and created new commits. The + // revision can be a full [hash value (see + // glossary)], + // of the recorded change to a ref within a repository pointing to a + // commit [commit] object. It does + // not necessarily have to be a hash; it can simply define a [revision + // number] + // which is an integer that is monotonically increasing. In cases where + // it is identical to the `ref.base.name`, it SHOULD still be included. + // It is up to the implementer to decide which value to set as the + // revision based on the VCS system and situational context. + // + // [revised version]: https://www.merriam-webster.com/dictionary/revision + // [hash value (see + // glossary)]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf + // [commit]: https://git-scm.com/docs/git-commit + // [revision + // number]: https://svnbook.red-bean.com/en/1.7/svn.tour.revs.specifiers.html + VCSRefBaseRevisionKey = attribute.Key("vcs.ref.base.revision") + + // VCSRefBaseTypeKey is the attribute Key conforming to the "vcs.ref.base.type" + // semantic conventions. It represents the type of the [reference] in the + // repository. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "branch", "tag" + // Note: `base` refers to the starting point of a change. For example, `main` + // would be the base reference of type branch if you've created a new + // reference of type branch from it and created new commits. + // + // [reference]: https://git-scm.com/docs/gitglossary#def_ref + VCSRefBaseTypeKey = attribute.Key("vcs.ref.base.type") + + // VCSRefHeadNameKey is the attribute Key conforming to the "vcs.ref.head.name" + // semantic conventions. It represents the name of the [reference] such as + // **branch** or **tag** in the repository. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "my-feature-branch", "tag-1-test" + // Note: `head` refers to where you are right now; the current reference at a + // given time. + // + // [reference]: https://git-scm.com/docs/gitglossary#def_ref + VCSRefHeadNameKey = attribute.Key("vcs.ref.head.name") + + // VCSRefHeadRevisionKey is the attribute Key conforming to the + // "vcs.ref.head.revision" semantic conventions. It represents the revision, + // literally [revised version], The revision most often refers to a commit + // object in Git, or a revision number in SVN. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "9d59409acf479dfa0df1aa568182e43e43df8bbe28d60fcf2bc52e30068802cc", + // "main", "123", "HEAD" + // Note: `head` refers to where you are right now; the current reference at a + // given time.The revision can be a full [hash value (see + // glossary)], + // of the recorded change to a ref within a repository pointing to a + // commit [commit] object. It does + // not necessarily have to be a hash; it can simply define a [revision + // number] + // which is an integer that is monotonically increasing. In cases where + // it is identical to the `ref.head.name`, it SHOULD still be included. + // It is up to the implementer to decide which value to set as the + // revision based on the VCS system and situational context. + // + // [revised version]: https://www.merriam-webster.com/dictionary/revision + // [hash value (see + // glossary)]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf + // [commit]: https://git-scm.com/docs/git-commit + // [revision + // number]: https://svnbook.red-bean.com/en/1.7/svn.tour.revs.specifiers.html + VCSRefHeadRevisionKey = attribute.Key("vcs.ref.head.revision") + + // VCSRefHeadTypeKey is the attribute Key conforming to the "vcs.ref.head.type" + // semantic conventions. It represents the type of the [reference] in the + // repository. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "branch", "tag" + // Note: `head` refers to where you are right now; the current reference at a + // given time. + // + // [reference]: https://git-scm.com/docs/gitglossary#def_ref + VCSRefHeadTypeKey = attribute.Key("vcs.ref.head.type") + + // VCSRefTypeKey is the attribute Key conforming to the "vcs.ref.type" semantic + // conventions. It represents the type of the [reference] in the repository. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "branch", "tag" + // + // [reference]: https://git-scm.com/docs/gitglossary#def_ref + VCSRefTypeKey = attribute.Key("vcs.ref.type") + + // VCSRepositoryNameKey is the attribute Key conforming to the + // "vcs.repository.name" semantic conventions. It represents the human readable + // name of the repository. It SHOULD NOT include any additional identifier like + // Group/SubGroup in GitLab or organization in GitHub. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "semantic-conventions", "my-cool-repo" + // Note: Due to it only being the name, it can clash with forks of the same + // repository if collecting telemetry across multiple orgs or groups in + // the same backends. + VCSRepositoryNameKey = attribute.Key("vcs.repository.name") + + // VCSRepositoryURLFullKey is the attribute Key conforming to the + // "vcs.repository.url.full" semantic conventions. It represents the + // [canonical URL] of the repository providing the complete HTTP(S) address in + // order to locate and identify the repository through a browser. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + // "https://github.com/opentelemetry/open-telemetry-collector-contrib", + // "https://gitlab.com/my-org/my-project/my-projects-project/repo" + // Note: In Git Version Control Systems, the canonical URL SHOULD NOT include + // the `.git` extension. + // + // [canonical URL]: https://support.google.com/webmasters/answer/10347851 + VCSRepositoryURLFullKey = attribute.Key("vcs.repository.url.full") + + // VCSRevisionDeltaDirectionKey is the attribute Key conforming to the + // "vcs.revision_delta.direction" semantic conventions. It represents the type + // of revision comparison. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "ahead", "behind" + VCSRevisionDeltaDirectionKey = attribute.Key("vcs.revision_delta.direction") +) + +// VCSChangeID returns an attribute KeyValue conforming to the "vcs.change.id" +// semantic conventions. It represents the ID of the change (pull request/merge +// request/changelist) if applicable. This is usually a unique (within +// repository) identifier generated by the VCS system. +func VCSChangeID(val string) attribute.KeyValue { + return VCSChangeIDKey.String(val) +} + +// VCSChangeTitle returns an attribute KeyValue conforming to the +// "vcs.change.title" semantic conventions. It represents the human readable +// title of the change (pull request/merge request/changelist). This title is +// often a brief summary of the change and may get merged in to a ref as the +// commit summary. +func VCSChangeTitle(val string) attribute.KeyValue { + return VCSChangeTitleKey.String(val) +} + +// VCSOwnerName returns an attribute KeyValue conforming to the "vcs.owner.name" +// semantic conventions. It represents the group owner within the version control +// system. +func VCSOwnerName(val string) attribute.KeyValue { + return VCSOwnerNameKey.String(val) +} + +// VCSRefBaseName returns an attribute KeyValue conforming to the +// "vcs.ref.base.name" semantic conventions. It represents the name of the +// [reference] such as **branch** or **tag** in the repository. +// +// [reference]: https://git-scm.com/docs/gitglossary#def_ref +func VCSRefBaseName(val string) attribute.KeyValue { + return VCSRefBaseNameKey.String(val) +} + +// VCSRefBaseRevision returns an attribute KeyValue conforming to the +// "vcs.ref.base.revision" semantic conventions. It represents the revision, +// literally [revised version], The revision most often refers to a commit object +// in Git, or a revision number in SVN. +// +// [revised version]: https://www.merriam-webster.com/dictionary/revision +func VCSRefBaseRevision(val string) attribute.KeyValue { + return VCSRefBaseRevisionKey.String(val) +} + +// VCSRefHeadName returns an attribute KeyValue conforming to the +// "vcs.ref.head.name" semantic conventions. It represents the name of the +// [reference] such as **branch** or **tag** in the repository. +// +// [reference]: https://git-scm.com/docs/gitglossary#def_ref +func VCSRefHeadName(val string) attribute.KeyValue { + return VCSRefHeadNameKey.String(val) +} + +// VCSRefHeadRevision returns an attribute KeyValue conforming to the +// "vcs.ref.head.revision" semantic conventions. It represents the revision, +// literally [revised version], The revision most often refers to a commit object +// in Git, or a revision number in SVN. +// +// [revised version]: https://www.merriam-webster.com/dictionary/revision +func VCSRefHeadRevision(val string) attribute.KeyValue { + return VCSRefHeadRevisionKey.String(val) +} + +// VCSRepositoryName returns an attribute KeyValue conforming to the +// "vcs.repository.name" semantic conventions. It represents the human readable +// name of the repository. It SHOULD NOT include any additional identifier like +// Group/SubGroup in GitLab or organization in GitHub. +func VCSRepositoryName(val string) attribute.KeyValue { + return VCSRepositoryNameKey.String(val) +} + +// VCSRepositoryURLFull returns an attribute KeyValue conforming to the +// "vcs.repository.url.full" semantic conventions. It represents the +// [canonical URL] of the repository providing the complete HTTP(S) address in +// order to locate and identify the repository through a browser. +// +// [canonical URL]: https://support.google.com/webmasters/answer/10347851 +func VCSRepositoryURLFull(val string) attribute.KeyValue { + return VCSRepositoryURLFullKey.String(val) +} + +// Enum values for vcs.change.state +var ( + // Open means the change is currently active and under review. It hasn't been + // merged into the target branch yet, and it's still possible to make changes or + // add comments. + // Stability: development + VCSChangeStateOpen = VCSChangeStateKey.String("open") + // WIP (work-in-progress, draft) means the change is still in progress and not + // yet ready for a full review. It might still undergo significant changes. + // Stability: development + VCSChangeStateWip = VCSChangeStateKey.String("wip") + // Closed means the merge request has been closed without merging. This can + // happen for various reasons, such as the changes being deemed unnecessary, the + // issue being resolved in another way, or the author deciding to withdraw the + // request. + // Stability: development + VCSChangeStateClosed = VCSChangeStateKey.String("closed") + // Merged indicates that the change has been successfully integrated into the + // target codebase. + // Stability: development + VCSChangeStateMerged = VCSChangeStateKey.String("merged") +) + +// Enum values for vcs.line_change.type +var ( + // How many lines were added. + // Stability: development + VCSLineChangeTypeAdded = VCSLineChangeTypeKey.String("added") + // How many lines were removed. + // Stability: development + VCSLineChangeTypeRemoved = VCSLineChangeTypeKey.String("removed") +) + +// Enum values for vcs.provider.name +var ( + // [GitHub] + // Stability: development + // + // [GitHub]: https://github.com + VCSProviderNameGithub = VCSProviderNameKey.String("github") + // [GitLab] + // Stability: development + // + // [GitLab]: https://gitlab.com + VCSProviderNameGitlab = VCSProviderNameKey.String("gitlab") + // [Gitea] + // Stability: development + // + // [Gitea]: https://gitea.io + VCSProviderNameGitea = VCSProviderNameKey.String("gitea") + // [Bitbucket] + // Stability: development + // + // [Bitbucket]: https://bitbucket.org + VCSProviderNameBitbucket = VCSProviderNameKey.String("bitbucket") +) + +// Enum values for vcs.ref.base.type +var ( + // [branch] + // Stability: development + // + // [branch]: https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbranchabranch + VCSRefBaseTypeBranch = VCSRefBaseTypeKey.String("branch") + // [tag] + // Stability: development + // + // [tag]: https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddeftagatag + VCSRefBaseTypeTag = VCSRefBaseTypeKey.String("tag") +) + +// Enum values for vcs.ref.head.type +var ( + // [branch] + // Stability: development + // + // [branch]: https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbranchabranch + VCSRefHeadTypeBranch = VCSRefHeadTypeKey.String("branch") + // [tag] + // Stability: development + // + // [tag]: https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddeftagatag + VCSRefHeadTypeTag = VCSRefHeadTypeKey.String("tag") +) + +// Enum values for vcs.ref.type +var ( + // [branch] + // Stability: development + // + // [branch]: https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbranchabranch + VCSRefTypeBranch = VCSRefTypeKey.String("branch") + // [tag] + // Stability: development + // + // [tag]: https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddeftagatag + VCSRefTypeTag = VCSRefTypeKey.String("tag") +) + +// Enum values for vcs.revision_delta.direction +var ( + // How many revisions the change is behind the target ref. + // Stability: development + VCSRevisionDeltaDirectionBehind = VCSRevisionDeltaDirectionKey.String("behind") + // How many revisions the change is ahead of the target ref. + // Stability: development + VCSRevisionDeltaDirectionAhead = VCSRevisionDeltaDirectionKey.String("ahead") +) + +// Namespace: webengine +const ( + // WebEngineDescriptionKey is the attribute Key conforming to the + // "webengine.description" semantic conventions. It represents the additional + // description of the web engine (e.g. detailed version and edition + // information). + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "WildFly Full 21.0.0.Final (WildFly Core 13.0.1.Final) - + // 2.2.2.Final" + WebEngineDescriptionKey = attribute.Key("webengine.description") + + // WebEngineNameKey is the attribute Key conforming to the "webengine.name" + // semantic conventions. It represents the name of the web engine. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "WildFly" + WebEngineNameKey = attribute.Key("webengine.name") + + // WebEngineVersionKey is the attribute Key conforming to the + // "webengine.version" semantic conventions. It represents the version of the + // web engine. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "21.0.0" + WebEngineVersionKey = attribute.Key("webengine.version") +) + +// WebEngineDescription returns an attribute KeyValue conforming to the +// "webengine.description" semantic conventions. It represents the additional +// description of the web engine (e.g. detailed version and edition information). +func WebEngineDescription(val string) attribute.KeyValue { + return WebEngineDescriptionKey.String(val) +} + +// WebEngineName returns an attribute KeyValue conforming to the "webengine.name" +// semantic conventions. It represents the name of the web engine. +func WebEngineName(val string) attribute.KeyValue { + return WebEngineNameKey.String(val) +} + +// WebEngineVersion returns an attribute KeyValue conforming to the +// "webengine.version" semantic conventions. It represents the version of the web +// engine. +func WebEngineVersion(val string) attribute.KeyValue { + return WebEngineVersionKey.String(val) +} + +// Namespace: zos +const ( + // ZOSSmfIDKey is the attribute Key conforming to the "zos.smf.id" semantic + // conventions. It represents the System Management Facility (SMF) Identifier + // uniquely identified a z/OS system within a SYSPLEX or mainframe environment + // and is used for system and performance analysis. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "SYS1" + ZOSSmfIDKey = attribute.Key("zos.smf.id") + + // ZOSSysplexNameKey is the attribute Key conforming to the "zos.sysplex.name" + // semantic conventions. It represents the name of the SYSPLEX to which the z/OS + // system belongs too. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "SYSPLEX1" + ZOSSysplexNameKey = attribute.Key("zos.sysplex.name") +) + +// ZOSSmfID returns an attribute KeyValue conforming to the "zos.smf.id" semantic +// conventions. It represents the System Management Facility (SMF) Identifier +// uniquely identified a z/OS system within a SYSPLEX or mainframe environment +// and is used for system and performance analysis. +func ZOSSmfID(val string) attribute.KeyValue { + return ZOSSmfIDKey.String(val) +} + +// ZOSSysplexName returns an attribute KeyValue conforming to the +// "zos.sysplex.name" semantic conventions. It represents the name of the SYSPLEX +// to which the z/OS system belongs too. +func ZOSSysplexName(val string) attribute.KeyValue { + return ZOSSysplexNameKey.String(val) +} \ No newline at end of file diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/doc.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/doc.go new file mode 100644 index 0000000000..852362ef77 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/doc.go @@ -0,0 +1,9 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package semconv implements OpenTelemetry semantic conventions. +// +// OpenTelemetry semantic conventions are agreed standardized naming +// patterns for OpenTelemetry things. This package represents the v1.39.0 +// version of the OpenTelemetry semantic conventions. +package semconv // import "go.opentelemetry.io/otel/semconv/v1.39.0" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/error_type.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/error_type.go new file mode 100644 index 0000000000..84cf636a72 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/error_type.go @@ -0,0 +1,56 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package semconv // import "go.opentelemetry.io/otel/semconv/v1.39.0" + +import ( + "reflect" + + "go.opentelemetry.io/otel/attribute" +) + +// ErrorType returns an [attribute.KeyValue] identifying the error type of err. +// +// If err is nil, the returned attribute has the default value +// [ErrorTypeOther]. +// +// If err's type has the method +// +// ErrorType() string +// +// then the returned attribute has the value of err.ErrorType(). Otherwise, the +// returned attribute has a value derived from the concrete type of err. +// +// The key of the returned attribute is [ErrorTypeKey]. +func ErrorType(err error) attribute.KeyValue { + if err == nil { + return ErrorTypeOther + } + + return ErrorTypeKey.String(errorType(err)) +} + +func errorType(err error) string { + var s string + if et, ok := err.(interface{ ErrorType() string }); ok { + // Prioritize the ErrorType method if available. + s = et.ErrorType() + } + if s == "" { + // Fallback to reflection if the ErrorType method is not supported or + // returns an empty value. + + t := reflect.TypeOf(err) + pkg, name := t.PkgPath(), t.Name() + if pkg != "" && name != "" { + s = pkg + "." + name + } else { + // The type has no package path or name (predeclared, not-defined, + // or alias for a not-defined type). + // + // This is not guaranteed to be unique, but is a best effort. + s = t.String() + } + } + return s +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/exception.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/exception.go new file mode 100644 index 0000000000..7b688ecc33 --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/exception.go @@ -0,0 +1,9 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package semconv // import "go.opentelemetry.io/otel/semconv/v1.39.0" + +const ( + // ExceptionEventName is the name of the Span event representing an exception. + ExceptionEventName = "exception" +) diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/httpconv/metric.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/httpconv/metric.go similarity index 92% rename from cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/httpconv/metric.go rename to cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/httpconv/metric.go index 55bde895dd..cb993812a8 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/httpconv/metric.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/httpconv/metric.go @@ -67,6 +67,8 @@ var ( RequestMethodPut RequestMethodAttr = "PUT" // RequestMethodTrace is the TRACE method. RequestMethodTrace RequestMethodAttr = "TRACE" + // RequestMethodQuery is the QUERY method. + RequestMethodQuery RequestMethodAttr = "QUERY" // RequestMethodOther is the any HTTP method that the instrumentation has no // prior knowledge of. RequestMethodOther RequestMethodAttr = "_OTHER" @@ -91,6 +93,11 @@ type ClientActiveRequests struct { metric.Int64UpDownCounter } +var newClientActiveRequestsOpts = []metric.Int64UpDownCounterOption{ + metric.WithDescription("Number of active HTTP requests."), + metric.WithUnit("{request}"), +} + // NewClientActiveRequests returns a new ClientActiveRequests instrument. func NewClientActiveRequests( m metric.Meter, @@ -101,15 +108,18 @@ func NewClientActiveRequests( return ClientActiveRequests{noop.Int64UpDownCounter{}}, nil } + if len(opt) == 0 { + opt = newClientActiveRequestsOpts + } else { + opt = append(opt, newClientActiveRequestsOpts...) + } + i, err := m.Int64UpDownCounter( "http.client.active_requests", - append([]metric.Int64UpDownCounterOption{ - metric.WithDescription("Number of active HTTP requests."), - metric.WithUnit("{request}"), - }, opt...)..., + opt..., ) if err != nil { - return ClientActiveRequests{noop.Int64UpDownCounter{}}, err + return ClientActiveRequests{noop.Int64UpDownCounter{}}, err } return ClientActiveRequests{i}, nil } @@ -223,6 +233,11 @@ type ClientConnectionDuration struct { metric.Float64Histogram } +var newClientConnectionDurationOpts = []metric.Float64HistogramOption{ + metric.WithDescription("The duration of the successfully established outbound HTTP connections."), + metric.WithUnit("s"), +} + // NewClientConnectionDuration returns a new ClientConnectionDuration instrument. func NewClientConnectionDuration( m metric.Meter, @@ -233,15 +248,18 @@ func NewClientConnectionDuration( return ClientConnectionDuration{noop.Float64Histogram{}}, nil } + if len(opt) == 0 { + opt = newClientConnectionDurationOpts + } else { + opt = append(opt, newClientConnectionDurationOpts...) + } + i, err := m.Float64Histogram( "http.client.connection.duration", - append([]metric.Float64HistogramOption{ - metric.WithDescription("The duration of the successfully established outbound HTTP connections."), - metric.WithUnit("s"), - }, opt...)..., + opt..., ) if err != nil { - return ClientConnectionDuration{noop.Float64Histogram{}}, err + return ClientConnectionDuration{noop.Float64Histogram{}}, err } return ClientConnectionDuration{i}, nil } @@ -310,6 +328,7 @@ func (m ClientConnectionDuration) Record( func (m ClientConnectionDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) { if set.Len() == 0 { m.Float64Histogram.Record(ctx, val) + return } o := recOptPool.Get().(*[]metric.RecordOption) @@ -353,6 +372,11 @@ type ClientOpenConnections struct { metric.Int64UpDownCounter } +var newClientOpenConnectionsOpts = []metric.Int64UpDownCounterOption{ + metric.WithDescription("Number of outbound HTTP connections that are currently active or idle on the client."), + metric.WithUnit("{connection}"), +} + // NewClientOpenConnections returns a new ClientOpenConnections instrument. func NewClientOpenConnections( m metric.Meter, @@ -363,15 +387,18 @@ func NewClientOpenConnections( return ClientOpenConnections{noop.Int64UpDownCounter{}}, nil } + if len(opt) == 0 { + opt = newClientOpenConnectionsOpts + } else { + opt = append(opt, newClientOpenConnectionsOpts...) + } + i, err := m.Int64UpDownCounter( "http.client.open_connections", - append([]metric.Int64UpDownCounterOption{ - metric.WithDescription("Number of outbound HTTP connections that are currently active or idle on the client."), - metric.WithUnit("{connection}"), - }, opt...)..., + opt..., ) if err != nil { - return ClientOpenConnections{noop.Int64UpDownCounter{}}, err + return ClientOpenConnections{noop.Int64UpDownCounter{}}, err } return ClientOpenConnections{i}, nil } @@ -488,6 +515,11 @@ type ClientRequestBodySize struct { metric.Int64Histogram } +var newClientRequestBodySizeOpts = []metric.Int64HistogramOption{ + metric.WithDescription("Size of HTTP client request bodies."), + metric.WithUnit("By"), +} + // NewClientRequestBodySize returns a new ClientRequestBodySize instrument. func NewClientRequestBodySize( m metric.Meter, @@ -498,15 +530,18 @@ func NewClientRequestBodySize( return ClientRequestBodySize{noop.Int64Histogram{}}, nil } + if len(opt) == 0 { + opt = newClientRequestBodySizeOpts + } else { + opt = append(opt, newClientRequestBodySizeOpts...) + } + i, err := m.Int64Histogram( "http.client.request.body.size", - append([]metric.Int64HistogramOption{ - metric.WithDescription("Size of HTTP client request bodies."), - metric.WithUnit("By"), - }, opt...)..., + opt..., ) if err != nil { - return ClientRequestBodySize{noop.Int64Histogram{}}, err + return ClientRequestBodySize{noop.Int64Histogram{}}, err } return ClientRequestBodySize{i}, nil } @@ -593,6 +628,7 @@ func (m ClientRequestBodySize) Record( func (m ClientRequestBodySize) RecordSet(ctx context.Context, val int64, set attribute.Set) { if set.Len() == 0 { m.Int64Histogram.Record(ctx, val) + return } o := recOptPool.Get().(*[]metric.RecordOption) @@ -662,6 +698,11 @@ type ClientRequestDuration struct { metric.Float64Histogram } +var newClientRequestDurationOpts = []metric.Float64HistogramOption{ + metric.WithDescription("Duration of HTTP client requests."), + metric.WithUnit("s"), +} + // NewClientRequestDuration returns a new ClientRequestDuration instrument. func NewClientRequestDuration( m metric.Meter, @@ -672,15 +713,18 @@ func NewClientRequestDuration( return ClientRequestDuration{noop.Float64Histogram{}}, nil } + if len(opt) == 0 { + opt = newClientRequestDurationOpts + } else { + opt = append(opt, newClientRequestDurationOpts...) + } + i, err := m.Float64Histogram( "http.client.request.duration", - append([]metric.Float64HistogramOption{ - metric.WithDescription("Duration of HTTP client requests."), - metric.WithUnit("s"), - }, opt...)..., + opt..., ) if err != nil { - return ClientRequestDuration{noop.Float64Histogram{}}, err + return ClientRequestDuration{noop.Float64Histogram{}}, err } return ClientRequestDuration{i}, nil } @@ -753,6 +797,7 @@ func (m ClientRequestDuration) Record( func (m ClientRequestDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) { if set.Len() == 0 { m.Float64Histogram.Record(ctx, val) + return } o := recOptPool.Get().(*[]metric.RecordOption) @@ -822,6 +867,11 @@ type ClientResponseBodySize struct { metric.Int64Histogram } +var newClientResponseBodySizeOpts = []metric.Int64HistogramOption{ + metric.WithDescription("Size of HTTP client response bodies."), + metric.WithUnit("By"), +} + // NewClientResponseBodySize returns a new ClientResponseBodySize instrument. func NewClientResponseBodySize( m metric.Meter, @@ -832,15 +882,18 @@ func NewClientResponseBodySize( return ClientResponseBodySize{noop.Int64Histogram{}}, nil } + if len(opt) == 0 { + opt = newClientResponseBodySizeOpts + } else { + opt = append(opt, newClientResponseBodySizeOpts...) + } + i, err := m.Int64Histogram( "http.client.response.body.size", - append([]metric.Int64HistogramOption{ - metric.WithDescription("Size of HTTP client response bodies."), - metric.WithUnit("By"), - }, opt...)..., + opt..., ) if err != nil { - return ClientResponseBodySize{noop.Int64Histogram{}}, err + return ClientResponseBodySize{noop.Int64Histogram{}}, err } return ClientResponseBodySize{i}, nil } @@ -927,6 +980,7 @@ func (m ClientResponseBodySize) Record( func (m ClientResponseBodySize) RecordSet(ctx context.Context, val int64, set attribute.Set) { if set.Len() == 0 { m.Int64Histogram.Record(ctx, val) + return } o := recOptPool.Get().(*[]metric.RecordOption) @@ -996,6 +1050,11 @@ type ServerActiveRequests struct { metric.Int64UpDownCounter } +var newServerActiveRequestsOpts = []metric.Int64UpDownCounterOption{ + metric.WithDescription("Number of active HTTP server requests."), + metric.WithUnit("{request}"), +} + // NewServerActiveRequests returns a new ServerActiveRequests instrument. func NewServerActiveRequests( m metric.Meter, @@ -1006,15 +1065,18 @@ func NewServerActiveRequests( return ServerActiveRequests{noop.Int64UpDownCounter{}}, nil } + if len(opt) == 0 { + opt = newServerActiveRequestsOpts + } else { + opt = append(opt, newServerActiveRequestsOpts...) + } + i, err := m.Int64UpDownCounter( "http.server.active_requests", - append([]metric.Int64UpDownCounterOption{ - metric.WithDescription("Number of active HTTP server requests."), - metric.WithUnit("{request}"), - }, opt...)..., + opt..., ) if err != nil { - return ServerActiveRequests{noop.Int64UpDownCounter{}}, err + return ServerActiveRequests{noop.Int64UpDownCounter{}}, err } return ServerActiveRequests{i}, nil } @@ -1118,6 +1180,11 @@ type ServerRequestBodySize struct { metric.Int64Histogram } +var newServerRequestBodySizeOpts = []metric.Int64HistogramOption{ + metric.WithDescription("Size of HTTP server request bodies."), + metric.WithUnit("By"), +} + // NewServerRequestBodySize returns a new ServerRequestBodySize instrument. func NewServerRequestBodySize( m metric.Meter, @@ -1128,15 +1195,18 @@ func NewServerRequestBodySize( return ServerRequestBodySize{noop.Int64Histogram{}}, nil } + if len(opt) == 0 { + opt = newServerRequestBodySizeOpts + } else { + opt = append(opt, newServerRequestBodySizeOpts...) + } + i, err := m.Int64Histogram( "http.server.request.body.size", - append([]metric.Int64HistogramOption{ - metric.WithDescription("Size of HTTP server request bodies."), - metric.WithUnit("By"), - }, opt...)..., + opt..., ) if err != nil { - return ServerRequestBodySize{noop.Int64Histogram{}}, err + return ServerRequestBodySize{noop.Int64Histogram{}}, err } return ServerRequestBodySize{i}, nil } @@ -1220,6 +1290,7 @@ func (m ServerRequestBodySize) Record( func (m ServerRequestBodySize) RecordSet(ctx context.Context, val int64, set attribute.Set) { if set.Len() == 0 { m.Int64Histogram.Record(ctx, val) + return } o := recOptPool.Get().(*[]metric.RecordOption) @@ -1249,8 +1320,9 @@ func (ServerRequestBodySize) AttrResponseStatusCode(val int) attribute.KeyValue } // AttrRoute returns an optional attribute for the "http.route" semantic -// convention. It represents the matched route, that is, the path template in the -// format used by the respective server framework. +// convention. It represents the matched route template for the request. This +// MUST be low-cardinality and include all static path segments, with dynamic +// path segments represented with placeholders. func (ServerRequestBodySize) AttrRoute(val string) attribute.KeyValue { return attribute.String("http.route", val) } @@ -1299,6 +1371,11 @@ type ServerRequestDuration struct { metric.Float64Histogram } +var newServerRequestDurationOpts = []metric.Float64HistogramOption{ + metric.WithDescription("Duration of HTTP server requests."), + metric.WithUnit("s"), +} + // NewServerRequestDuration returns a new ServerRequestDuration instrument. func NewServerRequestDuration( m metric.Meter, @@ -1309,15 +1386,18 @@ func NewServerRequestDuration( return ServerRequestDuration{noop.Float64Histogram{}}, nil } + if len(opt) == 0 { + opt = newServerRequestDurationOpts + } else { + opt = append(opt, newServerRequestDurationOpts...) + } + i, err := m.Float64Histogram( "http.server.request.duration", - append([]metric.Float64HistogramOption{ - metric.WithDescription("Duration of HTTP server requests."), - metric.WithUnit("s"), - }, opt...)..., + opt..., ) if err != nil { - return ServerRequestDuration{noop.Float64Histogram{}}, err + return ServerRequestDuration{noop.Float64Histogram{}}, err } return ServerRequestDuration{i}, nil } @@ -1387,6 +1467,7 @@ func (m ServerRequestDuration) Record( func (m ServerRequestDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) { if set.Len() == 0 { m.Float64Histogram.Record(ctx, val) + return } o := recOptPool.Get().(*[]metric.RecordOption) @@ -1416,8 +1497,9 @@ func (ServerRequestDuration) AttrResponseStatusCode(val int) attribute.KeyValue } // AttrRoute returns an optional attribute for the "http.route" semantic -// convention. It represents the matched route, that is, the path template in the -// format used by the respective server framework. +// convention. It represents the matched route template for the request. This +// MUST be low-cardinality and include all static path segments, with dynamic +// path segments represented with placeholders. func (ServerRequestDuration) AttrRoute(val string) attribute.KeyValue { return attribute.String("http.route", val) } @@ -1466,6 +1548,11 @@ type ServerResponseBodySize struct { metric.Int64Histogram } +var newServerResponseBodySizeOpts = []metric.Int64HistogramOption{ + metric.WithDescription("Size of HTTP server response bodies."), + metric.WithUnit("By"), +} + // NewServerResponseBodySize returns a new ServerResponseBodySize instrument. func NewServerResponseBodySize( m metric.Meter, @@ -1476,15 +1563,18 @@ func NewServerResponseBodySize( return ServerResponseBodySize{noop.Int64Histogram{}}, nil } + if len(opt) == 0 { + opt = newServerResponseBodySizeOpts + } else { + opt = append(opt, newServerResponseBodySizeOpts...) + } + i, err := m.Int64Histogram( "http.server.response.body.size", - append([]metric.Int64HistogramOption{ - metric.WithDescription("Size of HTTP server response bodies."), - metric.WithUnit("By"), - }, opt...)..., + opt..., ) if err != nil { - return ServerResponseBodySize{noop.Int64Histogram{}}, err + return ServerResponseBodySize{noop.Int64Histogram{}}, err } return ServerResponseBodySize{i}, nil } @@ -1568,6 +1658,7 @@ func (m ServerResponseBodySize) Record( func (m ServerResponseBodySize) RecordSet(ctx context.Context, val int64, set attribute.Set) { if set.Len() == 0 { m.Int64Histogram.Record(ctx, val) + return } o := recOptPool.Get().(*[]metric.RecordOption) @@ -1597,8 +1688,9 @@ func (ServerResponseBodySize) AttrResponseStatusCode(val int) attribute.KeyValue } // AttrRoute returns an optional attribute for the "http.route" semantic -// convention. It represents the matched route, that is, the path template in the -// format used by the respective server framework. +// convention. It represents the matched route template for the request. This +// MUST be low-cardinality and include all static path segments, with dynamic +// path segments represented with placeholders. func (ServerResponseBodySize) AttrRoute(val string) attribute.KeyValue { return attribute.String("http.route", val) } @@ -1638,4 +1730,4 @@ func (ServerResponseBodySize) AttrServerPort(val int) attribute.KeyValue { // the category of synthetic traffic, such as tests or bots. func (ServerResponseBodySize) AttrUserAgentSyntheticType(val UserAgentSyntheticTypeAttr) attribute.KeyValue { return attribute.String("user_agent.synthetic.type", string(val)) -} \ No newline at end of file +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/otelconv/metric.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/otelconv/metric.go similarity index 89% rename from cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/otelconv/metric.go rename to cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/otelconv/metric.go index a78eafd1fa..901da86985 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/otelconv/metric.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/otelconv/metric.go @@ -3,7 +3,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -// Package httpconv provides types and functionality for OpenTelemetry semantic +// Package otelconv provides types and functionality for OpenTelemetry semantic // conventions in the "otel" namespace. package otelconv @@ -122,48 +122,6 @@ var ( SpanSamplingResultRecordAndSample SpanSamplingResultAttr = "RECORD_AND_SAMPLE" ) -// RPCGRPCStatusCodeAttr is an attribute conforming to the rpc.grpc.status_code -// semantic conventions. It represents the gRPC status code of the last gRPC -// requests performed in scope of this export call. -type RPCGRPCStatusCodeAttr int64 - -var ( - // RPCGRPCStatusCodeOk is the OK. - RPCGRPCStatusCodeOk RPCGRPCStatusCodeAttr = 0 - // RPCGRPCStatusCodeCancelled is the CANCELLED. - RPCGRPCStatusCodeCancelled RPCGRPCStatusCodeAttr = 1 - // RPCGRPCStatusCodeUnknown is the UNKNOWN. - RPCGRPCStatusCodeUnknown RPCGRPCStatusCodeAttr = 2 - // RPCGRPCStatusCodeInvalidArgument is the INVALID_ARGUMENT. - RPCGRPCStatusCodeInvalidArgument RPCGRPCStatusCodeAttr = 3 - // RPCGRPCStatusCodeDeadlineExceeded is the DEADLINE_EXCEEDED. - RPCGRPCStatusCodeDeadlineExceeded RPCGRPCStatusCodeAttr = 4 - // RPCGRPCStatusCodeNotFound is the NOT_FOUND. - RPCGRPCStatusCodeNotFound RPCGRPCStatusCodeAttr = 5 - // RPCGRPCStatusCodeAlreadyExists is the ALREADY_EXISTS. - RPCGRPCStatusCodeAlreadyExists RPCGRPCStatusCodeAttr = 6 - // RPCGRPCStatusCodePermissionDenied is the PERMISSION_DENIED. - RPCGRPCStatusCodePermissionDenied RPCGRPCStatusCodeAttr = 7 - // RPCGRPCStatusCodeResourceExhausted is the RESOURCE_EXHAUSTED. - RPCGRPCStatusCodeResourceExhausted RPCGRPCStatusCodeAttr = 8 - // RPCGRPCStatusCodeFailedPrecondition is the FAILED_PRECONDITION. - RPCGRPCStatusCodeFailedPrecondition RPCGRPCStatusCodeAttr = 9 - // RPCGRPCStatusCodeAborted is the ABORTED. - RPCGRPCStatusCodeAborted RPCGRPCStatusCodeAttr = 10 - // RPCGRPCStatusCodeOutOfRange is the OUT_OF_RANGE. - RPCGRPCStatusCodeOutOfRange RPCGRPCStatusCodeAttr = 11 - // RPCGRPCStatusCodeUnimplemented is the UNIMPLEMENTED. - RPCGRPCStatusCodeUnimplemented RPCGRPCStatusCodeAttr = 12 - // RPCGRPCStatusCodeInternal is the INTERNAL. - RPCGRPCStatusCodeInternal RPCGRPCStatusCodeAttr = 13 - // RPCGRPCStatusCodeUnavailable is the UNAVAILABLE. - RPCGRPCStatusCodeUnavailable RPCGRPCStatusCodeAttr = 14 - // RPCGRPCStatusCodeDataLoss is the DATA_LOSS. - RPCGRPCStatusCodeDataLoss RPCGRPCStatusCodeAttr = 15 - // RPCGRPCStatusCodeUnauthenticated is the UNAUTHENTICATED. - RPCGRPCStatusCodeUnauthenticated RPCGRPCStatusCodeAttr = 16 -) - // SDKExporterLogExported is an instrument used to record metric values // conforming to the "otel.sdk.exporter.log.exported" semantic conventions. It // represents the number of log records for which the export has finished, either @@ -172,6 +130,11 @@ type SDKExporterLogExported struct { metric.Int64Counter } +var newSDKExporterLogExportedOpts = []metric.Int64CounterOption{ + metric.WithDescription("The number of log records for which the export has finished, either successful or failed."), + metric.WithUnit("{log_record}"), +} + // NewSDKExporterLogExported returns a new SDKExporterLogExported instrument. func NewSDKExporterLogExported( m metric.Meter, @@ -182,15 +145,18 @@ func NewSDKExporterLogExported( return SDKExporterLogExported{noop.Int64Counter{}}, nil } + if len(opt) == 0 { + opt = newSDKExporterLogExportedOpts + } else { + opt = append(opt, newSDKExporterLogExportedOpts...) + } + i, err := m.Int64Counter( "otel.sdk.exporter.log.exported", - append([]metric.Int64CounterOption{ - metric.WithDescription("The number of log records for which the export has finished, either successful or failed."), - metric.WithUnit("{log_record}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKExporterLogExported{noop.Int64Counter{}}, err + return SDKExporterLogExported{noop.Int64Counter{}}, err } return SDKExporterLogExported{i}, nil } @@ -319,6 +285,11 @@ type SDKExporterLogInflight struct { metric.Int64UpDownCounter } +var newSDKExporterLogInflightOpts = []metric.Int64UpDownCounterOption{ + metric.WithDescription("The number of log records which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."), + metric.WithUnit("{log_record}"), +} + // NewSDKExporterLogInflight returns a new SDKExporterLogInflight instrument. func NewSDKExporterLogInflight( m metric.Meter, @@ -329,15 +300,18 @@ func NewSDKExporterLogInflight( return SDKExporterLogInflight{noop.Int64UpDownCounter{}}, nil } + if len(opt) == 0 { + opt = newSDKExporterLogInflightOpts + } else { + opt = append(opt, newSDKExporterLogInflightOpts...) + } + i, err := m.Int64UpDownCounter( "otel.sdk.exporter.log.inflight", - append([]metric.Int64UpDownCounterOption{ - metric.WithDescription("The number of log records which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."), - metric.WithUnit("{log_record}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKExporterLogInflight{noop.Int64UpDownCounter{}}, err + return SDKExporterLogInflight{noop.Int64UpDownCounter{}}, err } return SDKExporterLogInflight{i}, nil } @@ -449,6 +423,11 @@ type SDKExporterMetricDataPointExported struct { metric.Int64Counter } +var newSDKExporterMetricDataPointExportedOpts = []metric.Int64CounterOption{ + metric.WithDescription("The number of metric data points for which the export has finished, either successful or failed."), + metric.WithUnit("{data_point}"), +} + // NewSDKExporterMetricDataPointExported returns a new // SDKExporterMetricDataPointExported instrument. func NewSDKExporterMetricDataPointExported( @@ -460,15 +439,18 @@ func NewSDKExporterMetricDataPointExported( return SDKExporterMetricDataPointExported{noop.Int64Counter{}}, nil } + if len(opt) == 0 { + opt = newSDKExporterMetricDataPointExportedOpts + } else { + opt = append(opt, newSDKExporterMetricDataPointExportedOpts...) + } + i, err := m.Int64Counter( "otel.sdk.exporter.metric_data_point.exported", - append([]metric.Int64CounterOption{ - metric.WithDescription("The number of metric data points for which the export has finished, either successful or failed."), - metric.WithUnit("{data_point}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKExporterMetricDataPointExported{noop.Int64Counter{}}, err + return SDKExporterMetricDataPointExported{noop.Int64Counter{}}, err } return SDKExporterMetricDataPointExported{i}, nil } @@ -598,6 +580,11 @@ type SDKExporterMetricDataPointInflight struct { metric.Int64UpDownCounter } +var newSDKExporterMetricDataPointInflightOpts = []metric.Int64UpDownCounterOption{ + metric.WithDescription("The number of metric data points which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."), + metric.WithUnit("{data_point}"), +} + // NewSDKExporterMetricDataPointInflight returns a new // SDKExporterMetricDataPointInflight instrument. func NewSDKExporterMetricDataPointInflight( @@ -609,15 +596,18 @@ func NewSDKExporterMetricDataPointInflight( return SDKExporterMetricDataPointInflight{noop.Int64UpDownCounter{}}, nil } + if len(opt) == 0 { + opt = newSDKExporterMetricDataPointInflightOpts + } else { + opt = append(opt, newSDKExporterMetricDataPointInflightOpts...) + } + i, err := m.Int64UpDownCounter( "otel.sdk.exporter.metric_data_point.inflight", - append([]metric.Int64UpDownCounterOption{ - metric.WithDescription("The number of metric data points which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."), - metric.WithUnit("{data_point}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKExporterMetricDataPointInflight{noop.Int64UpDownCounter{}}, err + return SDKExporterMetricDataPointInflight{noop.Int64UpDownCounter{}}, err } return SDKExporterMetricDataPointInflight{i}, nil } @@ -728,6 +718,11 @@ type SDKExporterOperationDuration struct { metric.Float64Histogram } +var newSDKExporterOperationDurationOpts = []metric.Float64HistogramOption{ + metric.WithDescription("The duration of exporting a batch of telemetry records."), + metric.WithUnit("s"), +} + // NewSDKExporterOperationDuration returns a new SDKExporterOperationDuration // instrument. func NewSDKExporterOperationDuration( @@ -739,15 +734,18 @@ func NewSDKExporterOperationDuration( return SDKExporterOperationDuration{noop.Float64Histogram{}}, nil } + if len(opt) == 0 { + opt = newSDKExporterOperationDurationOpts + } else { + opt = append(opt, newSDKExporterOperationDurationOpts...) + } + i, err := m.Float64Histogram( "otel.sdk.exporter.operation.duration", - append([]metric.Float64HistogramOption{ - metric.WithDescription("The duration of exporting a batch of telemetry records."), - metric.WithUnit("s"), - }, opt...)..., + opt..., ) if err != nil { - return SDKExporterOperationDuration{noop.Float64Histogram{}}, err + return SDKExporterOperationDuration{noop.Float64Histogram{}}, err } return SDKExporterOperationDuration{i}, nil } @@ -825,6 +823,7 @@ func (m SDKExporterOperationDuration) Record( func (m SDKExporterOperationDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) { if set.Len() == 0 { m.Float64Histogram.Record(ctx, val) + return } o := recOptPool.Get().(*[]metric.RecordOption) @@ -865,11 +864,11 @@ func (SDKExporterOperationDuration) AttrComponentType(val ComponentTypeAttr) att return attribute.String("otel.component.type", string(val)) } -// AttrRPCGRPCStatusCode returns an optional attribute for the -// "rpc.grpc.status_code" semantic convention. It represents the gRPC status code -// of the last gRPC requests performed in scope of this export call. -func (SDKExporterOperationDuration) AttrRPCGRPCStatusCode(val RPCGRPCStatusCodeAttr) attribute.KeyValue { - return attribute.Int64("rpc.grpc.status_code", int64(val)) +// AttrRPCResponseStatusCode returns an optional attribute for the +// "rpc.response.status_code" semantic convention. It represents the gRPC status +// code of the last gRPC request performed in scope of this export call. +func (SDKExporterOperationDuration) AttrRPCResponseStatusCode(val string) attribute.KeyValue { + return attribute.String("rpc.response.status_code", val) } // AttrServerAddress returns an optional attribute for the "server.address" @@ -893,6 +892,11 @@ type SDKExporterSpanExported struct { metric.Int64Counter } +var newSDKExporterSpanExportedOpts = []metric.Int64CounterOption{ + metric.WithDescription("The number of spans for which the export has finished, either successful or failed."), + metric.WithUnit("{span}"), +} + // NewSDKExporterSpanExported returns a new SDKExporterSpanExported instrument. func NewSDKExporterSpanExported( m metric.Meter, @@ -903,15 +907,18 @@ func NewSDKExporterSpanExported( return SDKExporterSpanExported{noop.Int64Counter{}}, nil } + if len(opt) == 0 { + opt = newSDKExporterSpanExportedOpts + } else { + opt = append(opt, newSDKExporterSpanExportedOpts...) + } + i, err := m.Int64Counter( "otel.sdk.exporter.span.exported", - append([]metric.Int64CounterOption{ - metric.WithDescription("The number of spans for which the export has finished, either successful or failed."), - metric.WithUnit("{span}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKExporterSpanExported{noop.Int64Counter{}}, err + return SDKExporterSpanExported{noop.Int64Counter{}}, err } return SDKExporterSpanExported{i}, nil } @@ -1040,6 +1047,11 @@ type SDKExporterSpanInflight struct { metric.Int64UpDownCounter } +var newSDKExporterSpanInflightOpts = []metric.Int64UpDownCounterOption{ + metric.WithDescription("The number of spans which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."), + metric.WithUnit("{span}"), +} + // NewSDKExporterSpanInflight returns a new SDKExporterSpanInflight instrument. func NewSDKExporterSpanInflight( m metric.Meter, @@ -1050,15 +1062,18 @@ func NewSDKExporterSpanInflight( return SDKExporterSpanInflight{noop.Int64UpDownCounter{}}, nil } + if len(opt) == 0 { + opt = newSDKExporterSpanInflightOpts + } else { + opt = append(opt, newSDKExporterSpanInflightOpts...) + } + i, err := m.Int64UpDownCounter( "otel.sdk.exporter.span.inflight", - append([]metric.Int64UpDownCounterOption{ - metric.WithDescription("The number of spans which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."), - metric.WithUnit("{span}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKExporterSpanInflight{noop.Int64UpDownCounter{}}, err + return SDKExporterSpanInflight{noop.Int64UpDownCounter{}}, err } return SDKExporterSpanInflight{i}, nil } @@ -1169,6 +1184,11 @@ type SDKLogCreated struct { metric.Int64Counter } +var newSDKLogCreatedOpts = []metric.Int64CounterOption{ + metric.WithDescription("The number of logs submitted to enabled SDK Loggers."), + metric.WithUnit("{log_record}"), +} + // NewSDKLogCreated returns a new SDKLogCreated instrument. func NewSDKLogCreated( m metric.Meter, @@ -1179,15 +1199,18 @@ func NewSDKLogCreated( return SDKLogCreated{noop.Int64Counter{}}, nil } + if len(opt) == 0 { + opt = newSDKLogCreatedOpts + } else { + opt = append(opt, newSDKLogCreatedOpts...) + } + i, err := m.Int64Counter( "otel.sdk.log.created", - append([]metric.Int64CounterOption{ - metric.WithDescription("The number of logs submitted to enabled SDK Loggers."), - metric.WithUnit("{log_record}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKLogCreated{noop.Int64Counter{}}, err + return SDKLogCreated{noop.Int64Counter{}}, err } return SDKLogCreated{i}, nil } @@ -1254,6 +1277,11 @@ type SDKMetricReaderCollectionDuration struct { metric.Float64Histogram } +var newSDKMetricReaderCollectionDurationOpts = []metric.Float64HistogramOption{ + metric.WithDescription("The duration of the collect operation of the metric reader."), + metric.WithUnit("s"), +} + // NewSDKMetricReaderCollectionDuration returns a new // SDKMetricReaderCollectionDuration instrument. func NewSDKMetricReaderCollectionDuration( @@ -1265,15 +1293,18 @@ func NewSDKMetricReaderCollectionDuration( return SDKMetricReaderCollectionDuration{noop.Float64Histogram{}}, nil } + if len(opt) == 0 { + opt = newSDKMetricReaderCollectionDurationOpts + } else { + opt = append(opt, newSDKMetricReaderCollectionDurationOpts...) + } + i, err := m.Float64Histogram( "otel.sdk.metric_reader.collection.duration", - append([]metric.Float64HistogramOption{ - metric.WithDescription("The duration of the collect operation of the metric reader."), - metric.WithUnit("s"), - }, opt...)..., + opt..., ) if err != nil { - return SDKMetricReaderCollectionDuration{noop.Float64Histogram{}}, err + return SDKMetricReaderCollectionDuration{noop.Float64Histogram{}}, err } return SDKMetricReaderCollectionDuration{i}, nil } @@ -1343,6 +1374,7 @@ func (m SDKMetricReaderCollectionDuration) Record( func (m SDKMetricReaderCollectionDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) { if set.Len() == 0 { m.Float64Histogram.Record(ctx, val) + return } o := recOptPool.Get().(*[]metric.RecordOption) @@ -1384,6 +1416,11 @@ type SDKProcessorLogProcessed struct { metric.Int64Counter } +var newSDKProcessorLogProcessedOpts = []metric.Int64CounterOption{ + metric.WithDescription("The number of log records for which the processing has finished, either successful or failed."), + metric.WithUnit("{log_record}"), +} + // NewSDKProcessorLogProcessed returns a new SDKProcessorLogProcessed instrument. func NewSDKProcessorLogProcessed( m metric.Meter, @@ -1394,15 +1431,18 @@ func NewSDKProcessorLogProcessed( return SDKProcessorLogProcessed{noop.Int64Counter{}}, nil } + if len(opt) == 0 { + opt = newSDKProcessorLogProcessedOpts + } else { + opt = append(opt, newSDKProcessorLogProcessedOpts...) + } + i, err := m.Int64Counter( "otel.sdk.processor.log.processed", - append([]metric.Int64CounterOption{ - metric.WithDescription("The number of log records for which the processing has finished, either successful or failed."), - metric.WithUnit("{log_record}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKProcessorLogProcessed{noop.Int64Counter{}}, err + return SDKProcessorLogProcessed{noop.Int64Counter{}}, err } return SDKProcessorLogProcessed{i}, nil } @@ -1515,6 +1555,11 @@ type SDKProcessorLogQueueCapacity struct { metric.Int64ObservableUpDownCounter } +var newSDKProcessorLogQueueCapacityOpts = []metric.Int64ObservableUpDownCounterOption{ + metric.WithDescription("The maximum number of log records the queue of a given instance of an SDK Log Record processor can hold."), + metric.WithUnit("{log_record}"), +} + // NewSDKProcessorLogQueueCapacity returns a new SDKProcessorLogQueueCapacity // instrument. func NewSDKProcessorLogQueueCapacity( @@ -1526,15 +1571,18 @@ func NewSDKProcessorLogQueueCapacity( return SDKProcessorLogQueueCapacity{noop.Int64ObservableUpDownCounter{}}, nil } + if len(opt) == 0 { + opt = newSDKProcessorLogQueueCapacityOpts + } else { + opt = append(opt, newSDKProcessorLogQueueCapacityOpts...) + } + i, err := m.Int64ObservableUpDownCounter( "otel.sdk.processor.log.queue.capacity", - append([]metric.Int64ObservableUpDownCounterOption{ - metric.WithDescription("The maximum number of log records the queue of a given instance of an SDK Log Record processor can hold."), - metric.WithUnit("{log_record}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKProcessorLogQueueCapacity{noop.Int64ObservableUpDownCounter{}}, err + return SDKProcessorLogQueueCapacity{noop.Int64ObservableUpDownCounter{}}, err } return SDKProcessorLogQueueCapacity{i}, nil } @@ -1581,6 +1629,11 @@ type SDKProcessorLogQueueSize struct { metric.Int64ObservableUpDownCounter } +var newSDKProcessorLogQueueSizeOpts = []metric.Int64ObservableUpDownCounterOption{ + metric.WithDescription("The number of log records in the queue of a given instance of an SDK log processor."), + metric.WithUnit("{log_record}"), +} + // NewSDKProcessorLogQueueSize returns a new SDKProcessorLogQueueSize instrument. func NewSDKProcessorLogQueueSize( m metric.Meter, @@ -1591,15 +1644,18 @@ func NewSDKProcessorLogQueueSize( return SDKProcessorLogQueueSize{noop.Int64ObservableUpDownCounter{}}, nil } + if len(opt) == 0 { + opt = newSDKProcessorLogQueueSizeOpts + } else { + opt = append(opt, newSDKProcessorLogQueueSizeOpts...) + } + i, err := m.Int64ObservableUpDownCounter( "otel.sdk.processor.log.queue.size", - append([]metric.Int64ObservableUpDownCounterOption{ - metric.WithDescription("The number of log records in the queue of a given instance of an SDK log processor."), - metric.WithUnit("{log_record}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKProcessorLogQueueSize{noop.Int64ObservableUpDownCounter{}}, err + return SDKProcessorLogQueueSize{noop.Int64ObservableUpDownCounter{}}, err } return SDKProcessorLogQueueSize{i}, nil } @@ -1646,6 +1702,11 @@ type SDKProcessorSpanProcessed struct { metric.Int64Counter } +var newSDKProcessorSpanProcessedOpts = []metric.Int64CounterOption{ + metric.WithDescription("The number of spans for which the processing has finished, either successful or failed."), + metric.WithUnit("{span}"), +} + // NewSDKProcessorSpanProcessed returns a new SDKProcessorSpanProcessed // instrument. func NewSDKProcessorSpanProcessed( @@ -1657,15 +1718,18 @@ func NewSDKProcessorSpanProcessed( return SDKProcessorSpanProcessed{noop.Int64Counter{}}, nil } + if len(opt) == 0 { + opt = newSDKProcessorSpanProcessedOpts + } else { + opt = append(opt, newSDKProcessorSpanProcessedOpts...) + } + i, err := m.Int64Counter( "otel.sdk.processor.span.processed", - append([]metric.Int64CounterOption{ - metric.WithDescription("The number of spans for which the processing has finished, either successful or failed."), - metric.WithUnit("{span}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKProcessorSpanProcessed{noop.Int64Counter{}}, err + return SDKProcessorSpanProcessed{noop.Int64Counter{}}, err } return SDKProcessorSpanProcessed{i}, nil } @@ -1778,6 +1842,11 @@ type SDKProcessorSpanQueueCapacity struct { metric.Int64ObservableUpDownCounter } +var newSDKProcessorSpanQueueCapacityOpts = []metric.Int64ObservableUpDownCounterOption{ + metric.WithDescription("The maximum number of spans the queue of a given instance of an SDK span processor can hold."), + metric.WithUnit("{span}"), +} + // NewSDKProcessorSpanQueueCapacity returns a new SDKProcessorSpanQueueCapacity // instrument. func NewSDKProcessorSpanQueueCapacity( @@ -1789,15 +1858,18 @@ func NewSDKProcessorSpanQueueCapacity( return SDKProcessorSpanQueueCapacity{noop.Int64ObservableUpDownCounter{}}, nil } + if len(opt) == 0 { + opt = newSDKProcessorSpanQueueCapacityOpts + } else { + opt = append(opt, newSDKProcessorSpanQueueCapacityOpts...) + } + i, err := m.Int64ObservableUpDownCounter( "otel.sdk.processor.span.queue.capacity", - append([]metric.Int64ObservableUpDownCounterOption{ - metric.WithDescription("The maximum number of spans the queue of a given instance of an SDK span processor can hold."), - metric.WithUnit("{span}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKProcessorSpanQueueCapacity{noop.Int64ObservableUpDownCounter{}}, err + return SDKProcessorSpanQueueCapacity{noop.Int64ObservableUpDownCounter{}}, err } return SDKProcessorSpanQueueCapacity{i}, nil } @@ -1844,6 +1916,11 @@ type SDKProcessorSpanQueueSize struct { metric.Int64ObservableUpDownCounter } +var newSDKProcessorSpanQueueSizeOpts = []metric.Int64ObservableUpDownCounterOption{ + metric.WithDescription("The number of spans in the queue of a given instance of an SDK span processor."), + metric.WithUnit("{span}"), +} + // NewSDKProcessorSpanQueueSize returns a new SDKProcessorSpanQueueSize // instrument. func NewSDKProcessorSpanQueueSize( @@ -1855,15 +1932,18 @@ func NewSDKProcessorSpanQueueSize( return SDKProcessorSpanQueueSize{noop.Int64ObservableUpDownCounter{}}, nil } + if len(opt) == 0 { + opt = newSDKProcessorSpanQueueSizeOpts + } else { + opt = append(opt, newSDKProcessorSpanQueueSizeOpts...) + } + i, err := m.Int64ObservableUpDownCounter( "otel.sdk.processor.span.queue.size", - append([]metric.Int64ObservableUpDownCounterOption{ - metric.WithDescription("The number of spans in the queue of a given instance of an SDK span processor."), - metric.WithUnit("{span}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKProcessorSpanQueueSize{noop.Int64ObservableUpDownCounter{}}, err + return SDKProcessorSpanQueueSize{noop.Int64ObservableUpDownCounter{}}, err } return SDKProcessorSpanQueueSize{i}, nil } @@ -1910,6 +1990,11 @@ type SDKSpanLive struct { metric.Int64UpDownCounter } +var newSDKSpanLiveOpts = []metric.Int64UpDownCounterOption{ + metric.WithDescription("The number of created spans with `recording=true` for which the end operation has not been called yet."), + metric.WithUnit("{span}"), +} + // NewSDKSpanLive returns a new SDKSpanLive instrument. func NewSDKSpanLive( m metric.Meter, @@ -1920,15 +2005,18 @@ func NewSDKSpanLive( return SDKSpanLive{noop.Int64UpDownCounter{}}, nil } + if len(opt) == 0 { + opt = newSDKSpanLiveOpts + } else { + opt = append(opt, newSDKSpanLiveOpts...) + } + i, err := m.Int64UpDownCounter( "otel.sdk.span.live", - append([]metric.Int64UpDownCounterOption{ - metric.WithDescription("The number of created spans with `recording=true` for which the end operation has not been called yet."), - metric.WithUnit("{span}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKSpanLive{noop.Int64UpDownCounter{}}, err + return SDKSpanLive{noop.Int64UpDownCounter{}}, err } return SDKSpanLive{i}, nil } @@ -2013,6 +2101,11 @@ type SDKSpanStarted struct { metric.Int64Counter } +var newSDKSpanStartedOpts = []metric.Int64CounterOption{ + metric.WithDescription("The number of created spans."), + metric.WithUnit("{span}"), +} + // NewSDKSpanStarted returns a new SDKSpanStarted instrument. func NewSDKSpanStarted( m metric.Meter, @@ -2023,15 +2116,18 @@ func NewSDKSpanStarted( return SDKSpanStarted{noop.Int64Counter{}}, nil } + if len(opt) == 0 { + opt = newSDKSpanStartedOpts + } else { + opt = append(opt, newSDKSpanStartedOpts...) + } + i, err := m.Int64Counter( "otel.sdk.span.started", - append([]metric.Int64CounterOption{ - metric.WithDescription("The number of created spans."), - metric.WithUnit("{span}"), - }, opt...)..., + opt..., ) if err != nil { - return SDKSpanStarted{noop.Int64Counter{}}, err + return SDKSpanStarted{noop.Int64Counter{}}, err } return SDKSpanStarted{i}, nil } @@ -2123,4 +2219,4 @@ func (SDKSpanStarted) AttrSpanParentOrigin(val SpanParentOriginAttr) attribute.K // value of the sampler for this span. func (SDKSpanStarted) AttrSpanSamplingResult(val SpanSamplingResultAttr) attribute.KeyValue { return attribute.String("otel.span.sampling_result", string(val)) -} \ No newline at end of file +} diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/schema.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/schema.go new file mode 100644 index 0000000000..e1a199d89b --- /dev/null +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/schema.go @@ -0,0 +1,9 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package semconv // import "go.opentelemetry.io/otel/semconv/v1.39.0" + +// SchemaURL is the schema URL that matches the version of the semantic conventions +// that this package defines. Semconv packages starting from v1.4.0 must declare +// non-empty schema URL in the form https://opentelemetry.io/schemas/ +const SchemaURL = "https://opentelemetry.io/schemas/1.39.0" diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/trace/auto.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/trace/auto.go index 8763936a84..604fdab446 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/trace/auto.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/trace/auto.go @@ -20,7 +20,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" "go.opentelemetry.io/otel/trace/embedded" "go.opentelemetry.io/otel/trace/internal/telemetry" ) diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/trace/config.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/trace/config.go index aea11a2b52..d9ecef1cad 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/trace/config.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/trace/config.go @@ -4,6 +4,7 @@ package trace // import "go.opentelemetry.io/otel/trace" import ( + "slices" "time" "go.opentelemetry.io/otel/attribute" @@ -304,12 +305,50 @@ func WithInstrumentationVersion(version string) TracerOption { }) } -// WithInstrumentationAttributes sets the instrumentation attributes. +// mergeSets returns the union of keys between a and b. Any duplicate keys will +// use the value associated with b. +func mergeSets(a, b attribute.Set) attribute.Set { + // NewMergeIterator uses the first value for any duplicates. + iter := attribute.NewMergeIterator(&b, &a) + merged := make([]attribute.KeyValue, 0, a.Len()+b.Len()) + for iter.Next() { + merged = append(merged, iter.Attribute()) + } + return attribute.NewSet(merged...) +} + +// WithInstrumentationAttributes adds the instrumentation attributes. // -// The passed attributes will be de-duplicated. +// This is equivalent to calling [WithInstrumentationAttributeSet] with an +// [attribute.Set] created from a clone of the passed attributes. +// [WithInstrumentationAttributeSet] is recommended for more control. +// +// If multiple [WithInstrumentationAttributes] or [WithInstrumentationAttributeSet] +// options are passed, the attributes will be merged together in the order +// they are passed. Attributes with duplicate keys will use the last value passed. func WithInstrumentationAttributes(attr ...attribute.KeyValue) TracerOption { + set := attribute.NewSet(slices.Clone(attr)...) + return WithInstrumentationAttributeSet(set) +} + +// WithInstrumentationAttributeSet adds the instrumentation attributes. +// +// If multiple [WithInstrumentationAttributes] or [WithInstrumentationAttributeSet] +// options are passed, the attributes will be merged together in the order +// they are passed. Attributes with duplicate keys will use the last value passed. +func WithInstrumentationAttributeSet(set attribute.Set) TracerOption { + if set.Len() == 0 { + return tracerOptionFunc(func(config TracerConfig) TracerConfig { + return config + }) + } + return tracerOptionFunc(func(config TracerConfig) TracerConfig { - config.attrs = attribute.NewSet(attr...) + if config.attrs.Len() == 0 { + config.attrs = set + } else { + config.attrs = mergeSets(config.attrs, set) + } return config }) } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/trace/span.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/trace/span.go index d3aa476ee1..d01e793664 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/trace/span.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/trace/span.go @@ -66,6 +66,10 @@ type Span interface { // SetAttributes sets kv as attributes of the Span. If a key from kv // already exists for an attribute of the Span it will be overwritten with // the value contained in kv. + // + // Note that adding attributes at span creation using [WithAttributes] is preferred + // to calling SetAttribute later, as samplers can only consider information + // already present during span creation. SetAttributes(kv ...attribute.KeyValue) // TracerProvider returns a TracerProvider that can be used to generate diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/version.go b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/version.go index bcaa5aa537..7c8f50803f 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/version.go +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/version.go @@ -5,5 +5,5 @@ package otel // import "go.opentelemetry.io/otel" // Version is the current release version of OpenTelemetry in use. func Version() string { - return "1.38.0" + return "1.40.0" } diff --git a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/versions.yaml b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/versions.yaml index 07145e254b..9daa2df9db 100644 --- a/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/versions.yaml +++ b/cluster-api/cluster-api/vendor/go.opentelemetry.io/otel/versions.yaml @@ -3,7 +3,7 @@ module-sets: stable-v1: - version: v1.38.0 + version: v1.40.0 modules: - go.opentelemetry.io/otel - go.opentelemetry.io/otel/bridge/opencensus @@ -22,11 +22,11 @@ module-sets: - go.opentelemetry.io/otel/sdk/metric - go.opentelemetry.io/otel/trace experimental-metrics: - version: v0.60.0 + version: v0.62.0 modules: - go.opentelemetry.io/otel/exporters/prometheus experimental-logs: - version: v0.14.0 + version: v0.16.0 modules: - go.opentelemetry.io/otel/log - go.opentelemetry.io/otel/log/logtest @@ -36,9 +36,31 @@ module-sets: - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp - go.opentelemetry.io/otel/exporters/stdout/stdoutlog experimental-schema: - version: v0.0.13 + version: v0.0.14 modules: - go.opentelemetry.io/otel/schema excluded-modules: - go.opentelemetry.io/otel/internal/tools - go.opentelemetry.io/otel/trace/internal/telemetry/test +modules: + go.opentelemetry.io/otel/exporters/stdout/stdouttrace: + version-refs: + - ./internal/version.go + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric: + version-refs: + - ./internal/version.go + go.opentelemetry.io/otel/exporters/prometheus: + version-refs: + - ./internal/version.go + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc: + version-refs: + - ./internal/version.go + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc: + version-refs: + - ./internal/version.go + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp: + version-refs: + - ./internal/version.go + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp: + version-refs: + - ./internal/version.go diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/.golangci.yml b/cluster-api/cluster-api/vendor/go.uber.org/zap/.golangci.yml index 2346df1351..74faaa71d8 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/.golangci.yml +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/.golangci.yml @@ -25,7 +25,7 @@ linters-settings: govet: # These govet checks are disabled by default, but they're useful. enable: - - niliness + - nilness - reflectvaluecompare - sortslice - unusedwrite diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/CHANGELOG.md b/cluster-api/cluster-api/vendor/go.uber.org/zap/CHANGELOG.md index 6d6cd5f4d7..86e7e6f982 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/CHANGELOG.md +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 1.27.1 (19 Nov 2025) +Enhancements: +* [#1501][]: prevent `Object` from panicking on nils +* [#1511][]: Fix a race condition in `WithLazy`. + +Thanks to @rabbbit, @alshopov, @jquirke, @arukiidou for their contributions to this release. + +[#1501]: https://github.com/uber-go/zap/pull/1501 +[#1511]: https://github.com/uber-go/zap/pull/1511 + ## 1.27.0 (20 Feb 2024) Enhancements: * [#1378][]: Add `WithLazy` method for `SugaredLogger`. diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md b/cluster-api/cluster-api/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md index e327d9aa5c..bc988b72ed 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md @@ -71,5 +71,5 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]. -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +[homepage]: https://contributor-covenant.org +[version]: https://contributor-covenant.org/version/1/4/ diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/LICENSE b/cluster-api/cluster-api/vendor/go.uber.org/zap/LICENSE index 6652bed45f..3883b9a7ea 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/LICENSE +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2017 Uber Technologies, Inc. +Copyright (c) 2016-2024 Uber Technologies, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/Makefile b/cluster-api/cluster-api/vendor/go.uber.org/zap/Makefile index eb1cee53bd..f9db385b34 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/Makefile +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/Makefile @@ -24,7 +24,7 @@ golangci-lint: @$(foreach mod,$(MODULE_DIRS), \ (cd $(mod) && \ echo "[lint] golangci-lint: $(mod)" && \ - golangci-lint run --path-prefix $(mod)) &&) true + golangci-lint run --path-prefix $(mod) ./...) &&) true .PHONY: tidy tidy: diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/field.go b/cluster-api/cluster-api/vendor/go.uber.org/zap/field.go index 6743930b82..1884afabcd 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/field.go +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/field.go @@ -398,6 +398,9 @@ func Durationp(key string, val *time.Duration) Field { // struct-like user-defined types to the logging context. The struct's // MarshalLogObject method is called lazily. func Object(key string, val zapcore.ObjectMarshaler) Field { + if val == nil { + return nilField(key) + } return Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val} } @@ -431,6 +434,13 @@ func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } +// DictObject constructs a [zapcore.ObjectMarshaler] with the given list of fields. +// The resulting object marshaler can be used as input to [Object], [Objects], or +// any other functions that expect an object marshaler. +func DictObject(val ...Field) zapcore.ObjectMarshaler { + return dictObject(val) +} + // We discovered an issue where zap.Any can cause a performance degradation // when used in new goroutines. // diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/http_handler.go b/cluster-api/cluster-api/vendor/go.uber.org/zap/http_handler.go index 2be8f65150..1cae2c164b 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/http_handler.go +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/http_handler.go @@ -71,7 +71,7 @@ import ( func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err := lvl.serveHTTP(w, r); err != nil { w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "internal error: %v", err) + _, _ = fmt.Fprintf(w, "internal error: %v", err) } } diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/logger.go b/cluster-api/cluster-api/vendor/go.uber.org/zap/logger.go index c4d3003239..2d0ef141bc 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/logger.go +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/logger.go @@ -381,7 +381,11 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { if stack.Count() == 0 { if log.addCaller { - fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", ent.Time.UTC()) + _, _ = fmt.Fprintf( + log.errorOutput, + "%v Logger.check error: failed to get caller\n", + ent.Time.UTC(), + ) _ = log.errorOutput.Sync() } return ce diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/options.go b/cluster-api/cluster-api/vendor/go.uber.org/zap/options.go index 43d357ac90..04a3c1e635 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/options.go +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/options.go @@ -125,7 +125,11 @@ func IncreaseLevel(lvl zapcore.LevelEnabler) Option { return optionFunc(func(log *Logger) { core, err := zapcore.NewIncreaseLevelCore(log.core, lvl) if err != nil { - fmt.Fprintf(log.errorOutput, "failed to IncreaseLevel: %v\n", err) + _, _ = fmt.Fprintf( + log.errorOutput, + "failed to IncreaseLevel: %v\n", + err, + ) } else { log.core = core } diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/sink.go b/cluster-api/cluster-api/vendor/go.uber.org/zap/sink.go index 499772a00d..92202280f1 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/sink.go +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/sink.go @@ -71,7 +71,7 @@ func newSinkRegistry() *sinkRegistry { return sr } -// RegisterScheme registers the given factory for the specific scheme. +// RegisterSink registers the given factory for the specific scheme. func (sr *sinkRegistry) RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error { sr.mu.Lock() defer sr.mu.Unlock() diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go b/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go index a40e93b3ec..4b426a5648 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go @@ -188,32 +188,33 @@ func (s *BufferedWriteSyncer) flushLoop() { // Stop closes the buffer, cleans up background goroutines, and flushes // remaining unwritten data. func (s *BufferedWriteSyncer) Stop() (err error) { - var stopped bool - // Critical section. - func() { + stopped := func() bool { s.mu.Lock() defer s.mu.Unlock() if !s.initialized { - return + return false } - stopped = s.stopped - if stopped { - return + if s.stopped { + return false } s.stopped = true s.ticker.Stop() close(s.stop) // tell flushLoop to stop - <-s.done // and wait until it has + return true }() - // Don't call Sync on consecutive Stops. + // Not initialized, or already stopped, no need for any cleanup. if !stopped { - err = s.Sync() + return } - return err + // Wait for flushLoop to end outside of the lock, as it may need the lock to complete. + // See https://github.com/uber-go/zap/issues/1428 for details. + <-s.done + + return s.Sync() } diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/console_encoder.go b/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/console_encoder.go index cc2b4e07b9..98eea5154d 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/console_encoder.go +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/console_encoder.go @@ -105,7 +105,7 @@ func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, if i > 0 { line.AppendString(c.ConsoleSeparator) } - fmt.Fprint(line, arr.elems[i]) + _, _ = fmt.Fprint(line, arr.elems[i]) } putSliceEncoder(arr) diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/entry.go b/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/entry.go index 459a5d7ce3..841752f2ed 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/entry.go +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/entry.go @@ -241,7 +241,12 @@ func (ce *CheckedEntry) Write(fields ...Field) { // If the entry is dirty, log an internal error; because the // CheckedEntry is being used after it was returned to the pool, // the message may be an amalgamation from multiple call sites. - fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", ce.Time, ce.Entry) + _, _ = fmt.Fprintf( + ce.ErrorOutput, + "%v Unsafe CheckedEntry re-use near Entry %+v.\n", + ce.Time, + ce.Entry, + ) _ = ce.ErrorOutput.Sync() // ignore error } return @@ -253,7 +258,12 @@ func (ce *CheckedEntry) Write(fields ...Field) { err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields)) } if err != nil && ce.ErrorOutput != nil { - fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", ce.Time, err) + _, _ = fmt.Fprintf( + ce.ErrorOutput, + "%v write error: %v\n", + ce.Time, + err, + ) _ = ce.ErrorOutput.Sync() // ignore error } diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/lazy_with.go b/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/lazy_with.go index 05288d6a88..500809de08 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/lazy_with.go +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/lazy_with.go @@ -23,7 +23,8 @@ package zapcore import "sync" type lazyWithCore struct { - Core + core Core + originalCore Core sync.Once fields []Field } @@ -32,23 +33,45 @@ type lazyWithCore struct { // the logger is written to (or is further chained in a lon-lazy manner). func NewLazyWith(core Core, fields []Field) Core { return &lazyWithCore{ - Core: core, - fields: fields, + core: nil, // core is allocated once `initOnce` is called. + originalCore: core, + fields: fields, } } func (d *lazyWithCore) initOnce() { d.Once.Do(func() { - d.Core = d.Core.With(d.fields) + d.core = d.originalCore.With(d.fields) }) } func (d *lazyWithCore) With(fields []Field) Core { d.initOnce() - return d.Core.With(fields) + return d.core.With(fields) } func (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry { + // This is safe because `lazyWithCore` doesn't change the level. + // So we can delagate the level check, any not `initOnce` + // just for the check. + if !d.originalCore.Enabled(e.Level) { + return ce + } + d.initOnce() + return d.core.Check(e, ce) +} + +func (d *lazyWithCore) Enabled(level Level) bool { + // Like above, this is safe because `lazyWithCore` doesn't change the level. + return d.originalCore.Enabled(level) +} + +func (d *lazyWithCore) Write(e Entry, fields []Field) error { + d.initOnce() + return d.core.Write(e, fields) +} + +func (d *lazyWithCore) Sync() error { d.initOnce() - return d.Core.Check(e, ce) + return d.core.Sync() } diff --git a/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/level.go b/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/level.go index e01a241316..f3e166d67b 100644 --- a/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/level.go +++ b/cluster-api/cluster-api/vendor/go.uber.org/zap/zapcore/level.go @@ -179,19 +179,19 @@ func (l *Level) UnmarshalText(text []byte) error { func (l *Level) unmarshalText(text []byte) bool { switch string(text) { - case "debug", "DEBUG": + case "debug": *l = DebugLevel - case "info", "INFO", "": // make the zero value useful + case "info", "": // make the zero value useful *l = InfoLevel - case "warn", "WARN": + case "warn", "warning": *l = WarnLevel - case "error", "ERROR": + case "error": *l = ErrorLevel - case "dpanic", "DPANIC": + case "dpanic": *l = DPanicLevel - case "panic", "PANIC": + case "panic": *l = PanicLevel - case "fatal", "FATAL": + case "fatal": *l = FatalLevel default: return false diff --git a/cluster-api/cluster-api/vendor/golang.org/x/net/http2/transport.go b/cluster-api/cluster-api/vendor/golang.org/x/net/http2/transport.go index 1965913e54..ccb87e6da3 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/net/http2/transport.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/net/http2/transport.go @@ -376,11 +376,24 @@ type ClientConn struct { // completely unresponsive connection. pendingResets int + // readBeforeStreamID is the smallest stream ID that has not been followed by + // a frame read from the peer. We use this to determine when a request may + // have been sent to a completely unresponsive connection: + // If the request ID is less than readBeforeStreamID, then we have had some + // indication of life on the connection since sending the request. + readBeforeStreamID uint32 + // reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests. // Write to reqHeaderMu to lock it, read from it to unlock. // Lock reqmu BEFORE mu or wmu. reqHeaderMu chan struct{} + // internalStateHook reports state changes back to the net/http.ClientConn. + // Note that this is different from the user state hook registered by + // net/http.ClientConn.SetStateHook: The internal hook calls ClientConn, + // which calls the user hook. + internalStateHook func() + // wmu is held while writing. // Acquire BEFORE mu when holding both, to avoid blocking mu on network writes. // Only acquire both at the same time when changing peer settings. @@ -710,7 +723,7 @@ func canRetryError(err error) bool { func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) { if t.transportTestHooks != nil { - return t.newClientConn(nil, singleUse) + return t.newClientConn(nil, singleUse, nil) } host, _, err := net.SplitHostPort(addr) if err != nil { @@ -720,7 +733,7 @@ func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse b if err != nil { return nil, err } - return t.newClientConn(tconn, singleUse) + return t.newClientConn(tconn, singleUse, nil) } func (t *Transport) newTLSConfig(host string) *tls.Config { @@ -772,10 +785,10 @@ func (t *Transport) expectContinueTimeout() time.Duration { } func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) { - return t.newClientConn(c, t.disableKeepAlives()) + return t.newClientConn(c, t.disableKeepAlives(), nil) } -func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) { +func (t *Transport) newClientConn(c net.Conn, singleUse bool, internalStateHook func()) (*ClientConn, error) { conf := configFromTransport(t) cc := &ClientConn{ t: t, @@ -797,6 +810,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro pings: make(map[[8]byte]chan struct{}), reqHeaderMu: make(chan struct{}, 1), lastActive: time.Now(), + internalStateHook: internalStateHook, } if t.transportTestHooks != nil { t.transportTestHooks.newclientconn(cc) @@ -1037,10 +1051,7 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) { maxConcurrentOkay = cc.currentRequestCountLocked() < int(cc.maxConcurrentStreams) } - st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing && maxConcurrentOkay && - !cc.doNotReuse && - int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 && - !cc.tooIdleLocked() + st.canTakeNewRequest = maxConcurrentOkay && cc.isUsableLocked() // If this connection has never been used for a request and is closed, // then let it take a request (which will fail). @@ -1056,6 +1067,31 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) { return } +func (cc *ClientConn) isUsableLocked() bool { + return cc.goAway == nil && + !cc.closed && + !cc.closing && + !cc.doNotReuse && + int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 && + !cc.tooIdleLocked() +} + +// canReserveLocked reports whether a net/http.ClientConn can reserve a slot on this conn. +// +// This follows slightly different rules than clientConnIdleState.canTakeNewRequest. +// We only permit reservations up to the conn's concurrency limit. +// This differs from ClientConn.ReserveNewRequest, which permits reservations +// past the limit when StrictMaxConcurrentStreams is set. +func (cc *ClientConn) canReserveLocked() bool { + if cc.currentRequestCountLocked() >= int(cc.maxConcurrentStreams) { + return false + } + if !cc.isUsableLocked() { + return false + } + return true +} + // currentRequestCountLocked reports the number of concurrency slots currently in use, // including active streams, reserved slots, and reset streams waiting for acknowledgement. func (cc *ClientConn) currentRequestCountLocked() int { @@ -1067,6 +1103,14 @@ func (cc *ClientConn) canTakeNewRequestLocked() bool { return st.canTakeNewRequest } +// availableLocked reports the number of concurrency slots available. +func (cc *ClientConn) availableLocked() int { + if !cc.canTakeNewRequestLocked() { + return 0 + } + return max(0, int(cc.maxConcurrentStreams)-cc.currentRequestCountLocked()) +} + // tooIdleLocked reports whether this connection has been been sitting idle // for too much wall time. func (cc *ClientConn) tooIdleLocked() bool { @@ -1091,6 +1135,7 @@ func (cc *ClientConn) closeConn() { t := time.AfterFunc(250*time.Millisecond, cc.forceCloseConn) defer t.Stop() cc.tconn.Close() + cc.maybeCallStateHook() } // A tls.Conn.Close can hang for a long time if the peer is unresponsive. @@ -1616,6 +1661,8 @@ func (cs *clientStream) cleanupWriteRequest(err error) { } bodyClosed := cs.reqBodyClosed closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil + // Have we read any frames from the connection since sending this request? + readSinceStream := cc.readBeforeStreamID > cs.ID cc.mu.Unlock() if mustCloseBody { cs.reqBody.Close() @@ -1647,8 +1694,10 @@ func (cs *clientStream) cleanupWriteRequest(err error) { // // This could be due to the server becoming unresponsive. // To avoid sending too many requests on a dead connection, - // we let the request continue to consume a concurrency slot - // until we can confirm the server is still responding. + // if we haven't read any frames from the connection since + // sending this request, we let it continue to consume + // a concurrency slot until we can confirm the server is + // still responding. // We do this by sending a PING frame along with the RST_STREAM // (unless a ping is already in flight). // @@ -1659,7 +1708,7 @@ func (cs *clientStream) cleanupWriteRequest(err error) { // because it's short lived and will probably be closed before // we get the ping response. ping := false - if !closeOnIdle { + if !closeOnIdle && !readSinceStream { cc.mu.Lock() // rstStreamPingsBlocked works around a gRPC behavior: // see comment on the field for details. @@ -1693,6 +1742,7 @@ func (cs *clientStream) cleanupWriteRequest(err error) { } close(cs.donec) + cc.maybeCallStateHook() } // awaitOpenSlotForStreamLocked waits until len(streams) < maxConcurrentStreams. @@ -2745,6 +2795,7 @@ func (rl *clientConnReadLoop) streamByID(id uint32, headerOrData bool) *clientSt // See comment on ClientConn.rstStreamPingsBlocked for details. rl.cc.rstStreamPingsBlocked = false } + rl.cc.readBeforeStreamID = rl.cc.nextStreamID cs := rl.cc.streams[id] if cs != nil && !cs.readAborted { return cs @@ -2795,6 +2846,7 @@ func (rl *clientConnReadLoop) processSettings(f *SettingsFrame) error { func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { cc := rl.cc + defer cc.maybeCallStateHook() cc.mu.Lock() defer cc.mu.Unlock() @@ -2975,6 +3027,7 @@ func (cc *ClientConn) Ping(ctx context.Context) error { func (rl *clientConnReadLoop) processPing(f *PingFrame) error { if f.IsAck() { cc := rl.cc + defer cc.maybeCallStateHook() cc.mu.Lock() defer cc.mu.Unlock() // If ack, notify listener if any @@ -3198,9 +3251,13 @@ func registerHTTPSProtocol(t *http.Transport, rt noDialH2RoundTripper) (err erro } // noDialH2RoundTripper is a RoundTripper which only tries to complete the request -// if there's already has a cached connection to the host. +// if there's already a cached connection to the host. // (The field is exported so it can be accessed via reflect from net/http; tested // by TestNoDialH2RoundTripperType) +// +// A noDialH2RoundTripper is registered with http1.Transport.RegisterProtocol, +// and the http1.Transport can use type assertions to call non-RoundTrip methods on it. +// This lets us expose, for example, NewClientConn to net/http. type noDialH2RoundTripper struct{ *Transport } func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { @@ -3211,6 +3268,85 @@ func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, err return res, err } +func (rt noDialH2RoundTripper) NewClientConn(conn net.Conn, internalStateHook func()) (http.RoundTripper, error) { + tr := rt.Transport + cc, err := tr.newClientConn(conn, tr.disableKeepAlives(), internalStateHook) + if err != nil { + return nil, err + } + + // RoundTrip should block when the conn is at its concurrency limit, + // not return an error. Setting strictMaxConcurrentStreams enables this. + cc.strictMaxConcurrentStreams = true + + return netHTTPClientConn{cc}, nil +} + +// netHTTPClientConn wraps ClientConn and implements the interface net/http expects from +// the RoundTripper returned by NewClientConn. +type netHTTPClientConn struct { + cc *ClientConn +} + +func (cc netHTTPClientConn) RoundTrip(req *http.Request) (*http.Response, error) { + return cc.cc.RoundTrip(req) +} + +func (cc netHTTPClientConn) Close() error { + return cc.cc.Close() +} + +func (cc netHTTPClientConn) Err() error { + cc.cc.mu.Lock() + defer cc.cc.mu.Unlock() + if cc.cc.closed { + return errors.New("connection closed") + } + return nil +} + +func (cc netHTTPClientConn) Reserve() error { + defer cc.cc.maybeCallStateHook() + cc.cc.mu.Lock() + defer cc.cc.mu.Unlock() + if !cc.cc.canReserveLocked() { + return errors.New("connection is unavailable") + } + cc.cc.streamsReserved++ + return nil +} + +func (cc netHTTPClientConn) Release() { + defer cc.cc.maybeCallStateHook() + cc.cc.mu.Lock() + defer cc.cc.mu.Unlock() + // We don't complain if streamsReserved is 0. + // + // This is consistent with RoundTrip: both Release and RoundTrip will + // consume a reservation iff one exists. + if cc.cc.streamsReserved > 0 { + cc.cc.streamsReserved-- + } +} + +func (cc netHTTPClientConn) Available() int { + cc.cc.mu.Lock() + defer cc.cc.mu.Unlock() + return cc.cc.availableLocked() +} + +func (cc netHTTPClientConn) InFlight() int { + cc.cc.mu.Lock() + defer cc.cc.mu.Unlock() + return cc.cc.currentRequestCountLocked() +} + +func (cc *ClientConn) maybeCallStateHook() { + if cc.internalStateHook != nil { + cc.internalStateHook() + } +} + func (t *Transport) idleConnTimeout() time.Duration { // to keep things backwards compatible, we use non-zero values of // IdleConnTimeout, followed by using the IdleConnTimeout on the underlying diff --git a/cluster-api/cluster-api/vendor/golang.org/x/net/http2/writesched_priority_rfc9218.go b/cluster-api/cluster-api/vendor/golang.org/x/net/http2/writesched_priority_rfc9218.go index cb4cadc32d..dfbfc1eb34 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/net/http2/writesched_priority_rfc9218.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/net/http2/writesched_priority_rfc9218.go @@ -37,6 +37,15 @@ type priorityWriteSchedulerRFC9218 struct { // incremental streams or not, when urgency is the same in a given Pop() // call. prioritizeIncremental bool + + // priorityUpdateBuf is used to buffer the most recent PRIORITY_UPDATE we + // receive per https://www.rfc-editor.org/rfc/rfc9218.html#name-the-priority_update-frame. + priorityUpdateBuf struct { + // streamID being 0 means that the buffer is empty. This is a safe + // assumption as PRIORITY_UPDATE for stream 0 is a PROTOCOL_ERROR. + streamID uint32 + priority PriorityParam + } } func newPriorityWriteSchedulerRFC9218() WriteScheduler { @@ -50,6 +59,10 @@ func (ws *priorityWriteSchedulerRFC9218) OpenStream(streamID uint32, opt OpenStr if ws.streams[streamID].location != nil { panic(fmt.Errorf("stream %d already opened", streamID)) } + if streamID == ws.priorityUpdateBuf.streamID { + ws.priorityUpdateBuf.streamID = 0 + opt.priority = ws.priorityUpdateBuf.priority + } q := ws.queuePool.get() ws.streams[streamID] = streamMetadata{ location: q, @@ -95,6 +108,8 @@ func (ws *priorityWriteSchedulerRFC9218) AdjustStream(streamID uint32, priority metadata := ws.streams[streamID] q, u, i := metadata.location, metadata.priority.urgency, metadata.priority.incremental if q == nil { + ws.priorityUpdateBuf.streamID = streamID + ws.priorityUpdateBuf.priority = priority return } diff --git a/cluster-api/cluster-api/vendor/golang.org/x/net/trace/events.go b/cluster-api/cluster-api/vendor/golang.org/x/net/trace/events.go index 3aaffdd1f7..c2b3c00980 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/net/trace/events.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/net/trace/events.go @@ -58,8 +58,8 @@ func RenderEvents(w http.ResponseWriter, req *http.Request, sensitive bool) { Buckets: buckets, } - data.Families = make([]string, 0, len(families)) famMu.RLock() + data.Families = make([]string, 0, len(families)) for name := range families { data.Families = append(data.Families, name) } diff --git a/cluster-api/cluster-api/vendor/golang.org/x/net/websocket/hybi.go b/cluster-api/cluster-api/vendor/golang.org/x/net/websocket/hybi.go index dda7434666..c7e76cd91b 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/net/websocket/hybi.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/net/websocket/hybi.go @@ -440,6 +440,7 @@ func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (er if err != nil { return err } + defer resp.Body.Close() if resp.StatusCode != 101 { return ErrBadStatus } diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sync/errgroup/errgroup.go b/cluster-api/cluster-api/vendor/golang.org/x/sync/errgroup/errgroup.go index 2f45dbc86e..f69fd75468 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sync/errgroup/errgroup.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -144,8 +144,8 @@ func (g *Group) SetLimit(n int) { g.sem = nil return } - if len(g.sem) != 0 { - panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem))) + if active := len(g.sem); active != 0 { + panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", active)) } g.sem = make(chan token, n) } diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/mkerrors.sh b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/mkerrors.sh index 42517077c4..fd39be4efd 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -256,6 +256,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -613,7 +614,7 @@ ccflags="$@" $2 !~ /IOC_MAGIC/ && $2 ~ /^[A-Z][A-Z0-9_]+_MAGIC2?$/ || $2 ~ /^(VM|VMADDR)_/ || - $2 ~ /^IOCTL_VM_SOCKETS_/ || + $2 ~ /^(IOCTL_VM_SOCKETS_|IOCTL_MEI_)/ || $2 ~ /^(TASKSTATS|TS)_/ || $2 ~ /^CGROUPSTATS_/ || $2 ~ /^GENL_/ || diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux.go index d0a75da572..120a7b35d1 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -1615,6 +1615,8 @@ const ( IN_OPEN = 0x20 IN_Q_OVERFLOW = 0x4000 IN_UNMOUNT = 0x2000 + IOCTL_MEI_CONNECT_CLIENT = 0xc0104801 + IOCTL_MEI_CONNECT_CLIENT_VTAG = 0xc0144804 IPPROTO_AH = 0x33 IPPROTO_BEETPH = 0x5e IPPROTO_COMP = 0x6c diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 1c37f9fbc4..97a61fc5b8 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -116,6 +116,8 @@ const ( IEXTEN = 0x8000 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 + IOCTL_MEI_NOTIFY_GET = 0x80044803 + IOCTL_MEI_NOTIFY_SET = 0x40044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 IPV6_FLOWINFO_MASK = 0xffffff0f IPV6_FLOWLABEL_MASK = 0xffff0f00 diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index 6f54d34aef..a0d6d498c4 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -116,6 +116,8 @@ const ( IEXTEN = 0x8000 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 + IOCTL_MEI_NOTIFY_GET = 0x80044803 + IOCTL_MEI_NOTIFY_SET = 0x40044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 IPV6_FLOWINFO_MASK = 0xffffff0f IPV6_FLOWLABEL_MASK = 0xffff0f00 diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index 783ec5c126..dd9c903f9a 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -115,6 +115,8 @@ const ( IEXTEN = 0x8000 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 + IOCTL_MEI_NOTIFY_GET = 0x80044803 + IOCTL_MEI_NOTIFY_SET = 0x40044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 IPV6_FLOWINFO_MASK = 0xffffff0f IPV6_FLOWLABEL_MASK = 0xffff0f00 diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index ca83d3ba16..384c61ca3a 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -120,6 +120,8 @@ const ( IEXTEN = 0x8000 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 + IOCTL_MEI_NOTIFY_GET = 0x80044803 + IOCTL_MEI_NOTIFY_SET = 0x40044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 IPV6_FLOWINFO_MASK = 0xffffff0f IPV6_FLOWLABEL_MASK = 0xffff0f00 diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go index 607e611c0c..6384c9831f 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go @@ -116,6 +116,8 @@ const ( IEXTEN = 0x8000 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 + IOCTL_MEI_NOTIFY_GET = 0x80044803 + IOCTL_MEI_NOTIFY_SET = 0x40044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 IPV6_FLOWINFO_MASK = 0xffffff0f IPV6_FLOWLABEL_MASK = 0xffff0f00 diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index b9cb5bd3c0..553c1c6f15 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -115,6 +115,8 @@ const ( IEXTEN = 0x100 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x80 + IOCTL_MEI_NOTIFY_GET = 0x40044803 + IOCTL_MEI_NOTIFY_SET = 0x80044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 IPV6_FLOWINFO_MASK = 0xfffffff IPV6_FLOWLABEL_MASK = 0xfffff diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index 65b078a638..b3339f2099 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -115,6 +115,8 @@ const ( IEXTEN = 0x100 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x80 + IOCTL_MEI_NOTIFY_GET = 0x40044803 + IOCTL_MEI_NOTIFY_SET = 0x80044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 IPV6_FLOWINFO_MASK = 0xfffffff IPV6_FLOWLABEL_MASK = 0xfffff diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index 5298a3033d..177091d2bc 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -115,6 +115,8 @@ const ( IEXTEN = 0x100 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x80 + IOCTL_MEI_NOTIFY_GET = 0x40044803 + IOCTL_MEI_NOTIFY_SET = 0x80044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 IPV6_FLOWINFO_MASK = 0xffffff0f IPV6_FLOWLABEL_MASK = 0xffff0f00 diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index 7bc557c876..c5abf156d0 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -115,6 +115,8 @@ const ( IEXTEN = 0x100 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x80 + IOCTL_MEI_NOTIFY_GET = 0x40044803 + IOCTL_MEI_NOTIFY_SET = 0x80044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 IPV6_FLOWINFO_MASK = 0xffffff0f IPV6_FLOWLABEL_MASK = 0xffff0f00 diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go index 152399bb04..f1f3fadf57 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go @@ -115,6 +115,8 @@ const ( IEXTEN = 0x400 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 + IOCTL_MEI_NOTIFY_GET = 0x40044803 + IOCTL_MEI_NOTIFY_SET = 0x80044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 IPV6_FLOWINFO_MASK = 0xfffffff IPV6_FLOWLABEL_MASK = 0xfffff diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index 1a1ce2409c..203ad9c54a 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -115,6 +115,8 @@ const ( IEXTEN = 0x400 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 + IOCTL_MEI_NOTIFY_GET = 0x40044803 + IOCTL_MEI_NOTIFY_SET = 0x80044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 IPV6_FLOWINFO_MASK = 0xfffffff IPV6_FLOWLABEL_MASK = 0xfffff diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index 4231a1fb57..4b9abcb21a 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -115,6 +115,8 @@ const ( IEXTEN = 0x400 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 + IOCTL_MEI_NOTIFY_GET = 0x40044803 + IOCTL_MEI_NOTIFY_SET = 0x80044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 IPV6_FLOWINFO_MASK = 0xffffff0f IPV6_FLOWLABEL_MASK = 0xffff0f00 diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index 21c0e95266..f87983037d 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -115,6 +115,8 @@ const ( IEXTEN = 0x8000 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 + IOCTL_MEI_NOTIFY_GET = 0x80044803 + IOCTL_MEI_NOTIFY_SET = 0x40044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 IPV6_FLOWINFO_MASK = 0xffffff0f IPV6_FLOWLABEL_MASK = 0xffff0f00 diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index f00d1cd7cf..64347eb354 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -115,6 +115,8 @@ const ( IEXTEN = 0x8000 IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 + IOCTL_MEI_NOTIFY_GET = 0x80044803 + IOCTL_MEI_NOTIFY_SET = 0x40044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 IPV6_FLOWINFO_MASK = 0xfffffff IPV6_FLOWLABEL_MASK = 0xfffff diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index bc8d539e6a..7d71911718 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -119,6 +119,8 @@ const ( IEXTEN = 0x8000 IN_CLOEXEC = 0x400000 IN_NONBLOCK = 0x4000 + IOCTL_MEI_NOTIFY_GET = 0x40044803 + IOCTL_MEI_NOTIFY_SET = 0x80044802 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 IPV6_FLOWINFO_MASK = 0xfffffff IPV6_FLOWLABEL_MASK = 0xfffff diff --git a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go index 439548ec9a..50e8e64497 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go @@ -104,7 +104,7 @@ type Statvfs_t struct { Fsid uint32 Namemax uint32 Owner uint32 - Spare [4]uint32 + Spare [4]uint64 Fstypename [32]byte Mntonname [1024]byte Mntfromname [1024]byte diff --git a/cluster-api/cluster-api/vendor/golang.org/x/term/terminal.go b/cluster-api/cluster-api/vendor/golang.org/x/term/terminal.go index 9255449b9b..6ec537cdc1 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/term/terminal.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/term/terminal.go @@ -160,7 +160,9 @@ const ( keyEnd keyDeleteWord keyDeleteLine + keyDelete keyClearScreen + keyTranspose keyPasteStart keyPasteEnd ) @@ -194,6 +196,8 @@ func bytesToKey(b []byte, pasteActive bool) (rune, []byte) { return keyDeleteLine, b[1:] case 12: // ^L return keyClearScreen, b[1:] + case 20: // ^T + return keyTranspose, b[1:] case 23: // ^W return keyDeleteWord, b[1:] case 14: // ^N @@ -228,6 +232,10 @@ func bytesToKey(b []byte, pasteActive bool) (rune, []byte) { } } + if !pasteActive && len(b) >= 4 && b[0] == keyEscape && b[1] == '[' && b[2] == '3' && b[3] == '~' { + return keyDelete, b[4:] + } + if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { switch b[5] { case 'C': @@ -590,7 +598,7 @@ func (t *Terminal) handleKey(key rune) (line string, ok bool) { } t.line = t.line[:t.pos] t.moveCursorToPos(t.pos) - case keyCtrlD: + case keyCtrlD, keyDelete: // Erase the character under the current position. // The EOF case when the line is empty is handled in // readLine(). @@ -600,6 +608,24 @@ func (t *Terminal) handleKey(key rune) (line string, ok bool) { } case keyCtrlU: t.eraseNPreviousChars(t.pos) + case keyTranspose: + // This transposes the two characters around the cursor and advances the cursor. Best-effort. + if len(t.line) < 2 || t.pos < 1 { + return + } + swap := t.pos + if swap == len(t.line) { + swap-- // special: at end of line, swap previous two chars + } + t.line[swap-1], t.line[swap] = t.line[swap], t.line[swap-1] + if t.pos < len(t.line) { + t.pos++ + } + if t.echo { + t.moveCursorToPos(swap - 1) + t.writeLine(t.line[swap-1:]) + t.moveCursorToPos(t.pos) + } case keyClearScreen: // Erases the screen and moves the cursor to the home position. t.queue([]rune("\x1b[2J\x1b[H")) diff --git a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/japanese/eucjp.go b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/japanese/eucjp.go index 79313fa589..6fce8c5f52 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/japanese/eucjp.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/japanese/eucjp.go @@ -17,9 +17,9 @@ import ( var EUCJP encoding.Encoding = &eucJP var eucJP = internal.Encoding{ - &internal.SimpleEncoding{eucJPDecoder{}, eucJPEncoder{}}, - "EUC-JP", - identifier.EUCPkdFmtJapanese, + Encoding: &internal.SimpleEncoding{Decoder: eucJPDecoder{}, Encoder: eucJPEncoder{}}, + Name: "EUC-JP", + MIB: identifier.EUCPkdFmtJapanese, } type eucJPDecoder struct{ transform.NopResetter } diff --git a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/japanese/iso2022jp.go b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/japanese/iso2022jp.go index 613226df5e..6f7bd460a6 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/japanese/iso2022jp.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/japanese/iso2022jp.go @@ -17,9 +17,9 @@ import ( var ISO2022JP encoding.Encoding = &iso2022JP var iso2022JP = internal.Encoding{ - internal.FuncEncoding{iso2022JPNewDecoder, iso2022JPNewEncoder}, - "ISO-2022-JP", - identifier.ISO2022JP, + Encoding: internal.FuncEncoding{Decoder: iso2022JPNewDecoder, Encoder: iso2022JPNewEncoder}, + Name: "ISO-2022-JP", + MIB: identifier.ISO2022JP, } func iso2022JPNewDecoder() transform.Transformer { diff --git a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/japanese/shiftjis.go b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/japanese/shiftjis.go index 16fd8a6e3e..af65d43d95 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/japanese/shiftjis.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/japanese/shiftjis.go @@ -18,9 +18,9 @@ import ( var ShiftJIS encoding.Encoding = &shiftJIS var shiftJIS = internal.Encoding{ - &internal.SimpleEncoding{shiftJISDecoder{}, shiftJISEncoder{}}, - "Shift JIS", - identifier.ShiftJIS, + Encoding: &internal.SimpleEncoding{Decoder: shiftJISDecoder{}, Encoder: shiftJISEncoder{}}, + Name: "Shift JIS", + MIB: identifier.ShiftJIS, } type shiftJISDecoder struct{ transform.NopResetter } diff --git a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/korean/euckr.go b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/korean/euckr.go index 034337f5df..81c834730c 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/korean/euckr.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/korean/euckr.go @@ -20,9 +20,9 @@ var All = []encoding.Encoding{EUCKR} var EUCKR encoding.Encoding = &eucKR var eucKR = internal.Encoding{ - &internal.SimpleEncoding{eucKRDecoder{}, eucKREncoder{}}, - "EUC-KR", - identifier.EUCKR, + Encoding: &internal.SimpleEncoding{Decoder: eucKRDecoder{}, Encoder: eucKREncoder{}}, + Name: "EUC-KR", + MIB: identifier.EUCKR, } type eucKRDecoder struct{ transform.NopResetter } diff --git a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/simplifiedchinese/gbk.go b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/simplifiedchinese/gbk.go index 0e0fabfd6b..2f2fd5d449 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/simplifiedchinese/gbk.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/simplifiedchinese/gbk.go @@ -22,21 +22,21 @@ var ( ) var gbk = internal.Encoding{ - &internal.SimpleEncoding{ - gbkDecoder{gb18030: false}, - gbkEncoder{gb18030: false}, + Encoding: &internal.SimpleEncoding{ + Decoder: gbkDecoder{gb18030: false}, + Encoder: gbkEncoder{gb18030: false}, }, - "GBK", - identifier.GBK, + Name: "GBK", + MIB: identifier.GBK, } var gbk18030 = internal.Encoding{ - &internal.SimpleEncoding{ - gbkDecoder{gb18030: true}, - gbkEncoder{gb18030: true}, + Encoding: &internal.SimpleEncoding{ + Decoder: gbkDecoder{gb18030: true}, + Encoder: gbkEncoder{gb18030: true}, }, - "GB18030", - identifier.GB18030, + Name: "GB18030", + MIB: identifier.GB18030, } type gbkDecoder struct { diff --git a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/simplifiedchinese/hzgb2312.go b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/simplifiedchinese/hzgb2312.go index e15b7bf6a7..351750e60e 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/simplifiedchinese/hzgb2312.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/simplifiedchinese/hzgb2312.go @@ -17,9 +17,9 @@ import ( var HZGB2312 encoding.Encoding = &hzGB2312 var hzGB2312 = internal.Encoding{ - internal.FuncEncoding{hzGB2312NewDecoder, hzGB2312NewEncoder}, - "HZ-GB2312", - identifier.HZGB2312, + Encoding: internal.FuncEncoding{Decoder: hzGB2312NewDecoder, Encoder: hzGB2312NewEncoder}, + Name: "HZ-GB2312", + MIB: identifier.HZGB2312, } func hzGB2312NewDecoder() transform.Transformer { diff --git a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/traditionalchinese/big5.go b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/traditionalchinese/big5.go index 1fcddde082..5046920ee0 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/traditionalchinese/big5.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/traditionalchinese/big5.go @@ -20,9 +20,9 @@ var All = []encoding.Encoding{Big5} var Big5 encoding.Encoding = &big5 var big5 = internal.Encoding{ - &internal.SimpleEncoding{big5Decoder{}, big5Encoder{}}, - "Big5", - identifier.Big5, + Encoding: &internal.SimpleEncoding{Decoder: big5Decoder{}, Encoder: big5Encoder{}}, + Name: "Big5", + MIB: identifier.Big5, } type big5Decoder struct{ transform.NopResetter } diff --git a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/unicode/unicode.go b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/unicode/unicode.go index dd99ad14d3..ce28c90628 100644 --- a/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/unicode/unicode.go +++ b/cluster-api/cluster-api/vendor/golang.org/x/text/encoding/unicode/unicode.go @@ -60,9 +60,9 @@ func (utf8bomEncoding) NewDecoder() *encoding.Decoder { } var utf8enc = &internal.Encoding{ - &internal.SimpleEncoding{utf8Decoder{}, runes.ReplaceIllFormed()}, - "UTF-8", - identifier.UTF8, + Encoding: &internal.SimpleEncoding{Decoder: utf8Decoder{}, Encoder: runes.ReplaceIllFormed()}, + Name: "UTF-8", + MIB: identifier.UTF8, } type utf8bomDecoder struct { diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/balancer.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/balancer.go index b1264017db..d08b7ad637 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/balancer.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/balancer.go @@ -75,8 +75,6 @@ func unregisterForTesting(name string) { func init() { internal.BalancerUnregister = unregisterForTesting - internal.ConnectedAddress = connectedAddress - internal.SetConnectedAddress = setConnectedAddress } // Get returns the resolver builder registered with the given name. diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/pickfirst/internal/internal.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/pickfirst/internal/internal.go index 7d66cb491c..cc902a4de6 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/pickfirst/internal/internal.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/pickfirst/internal/internal.go @@ -26,6 +26,8 @@ import ( var ( // RandShuffle pseudo-randomizes the order of addresses. RandShuffle = rand.Shuffle + // RandFloat64 returns, as a float64, a pseudo-random number in [0.0,1.0). + RandFloat64 = rand.Float64 // TimeAfterFunc allows mocking the timer for testing connection delay // related functionality. TimeAfterFunc = func(d time.Duration, f func()) func() { diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go index b4bc3a2bf3..dccd9f0bf3 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go @@ -21,11 +21,14 @@ package pickfirst import ( + "cmp" "encoding/json" "errors" "fmt" + "math" "net" "net/netip" + "slices" "sync" "time" @@ -34,6 +37,8 @@ import ( "google.golang.org/grpc/connectivity" expstats "google.golang.org/grpc/experimental/stats" "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/internal/balancer/weight" + "google.golang.org/grpc/internal/envconfig" internalgrpclog "google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/pretty" "google.golang.org/grpc/resolver" @@ -258,8 +263,42 @@ func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState // will change the order of endpoints but not touch the order of the // addresses within each endpoint. - A61 if cfg.ShuffleAddressList { - endpoints = append([]resolver.Endpoint{}, endpoints...) - internal.RandShuffle(len(endpoints), func(i, j int) { endpoints[i], endpoints[j] = endpoints[j], endpoints[i] }) + if envconfig.PickFirstWeightedShuffling { + type weightedEndpoint struct { + endpoint resolver.Endpoint + weight float64 + } + + // For each endpoint, compute a key as described in A113 and + // https://utopia.duth.gr/~pefraimi/research/data/2007EncOfAlg.pdf: + var weightedEndpoints []weightedEndpoint + for _, endpoint := range endpoints { + u := internal.RandFloat64() // Random number in [0.0, 1.0) + weight := weightAttribute(endpoint) + weightedEndpoints = append(weightedEndpoints, weightedEndpoint{ + endpoint: endpoint, + weight: math.Pow(u, 1.0/float64(weight)), + }) + } + // Sort endpoints by key in descending order and reconstruct the + // endpoints slice. + slices.SortFunc(weightedEndpoints, func(a, b weightedEndpoint) int { + return cmp.Compare(b.weight, a.weight) + }) + + // Here, and in the "else" block below, we clone the endpoints + // slice to avoid mutating the resolver state. Doing the latter + // would lead to data races if the caller is accessing the same + // slice concurrently. + sortedEndpoints := make([]resolver.Endpoint, len(endpoints)) + for i, we := range weightedEndpoints { + sortedEndpoints[i] = we.endpoint + } + endpoints = sortedEndpoints + } else { + endpoints = slices.Clone(endpoints) + internal.RandShuffle(len(endpoints), func(i, j int) { endpoints[i], endpoints[j] = endpoints[j], endpoints[i] }) + } } // "Flatten the list by concatenating the ordered list of addresses for @@ -906,3 +945,17 @@ func equalAddressIgnoringBalAttributes(a, b *resolver.Address) bool { return a.Addr == b.Addr && a.ServerName == b.ServerName && a.Attributes.Equal(b.Attributes) } + +// weightAttribute is a convenience function which returns the value of the +// weight endpoint Attribute. +// +// When used in the xDS context, the weight attribute is guaranteed to be +// non-zero. But, when used in a non-xDS context, the weight attribute could be +// unset. A Default of 1 is used in the latter case. +func weightAttribute(e resolver.Endpoint) uint32 { + w := weight.FromEndpoint(e).Weight + if w == 0 { + return 1 + } + return w +} diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/subconn.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/subconn.go index 9ee44d4af0..c1ca7c92e7 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/subconn.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer/subconn.go @@ -111,20 +111,6 @@ type SubConnState struct { // ConnectionError is set if the ConnectivityState is TransientFailure, // describing the reason the SubConn failed. Otherwise, it is nil. ConnectionError error - // connectedAddr contains the connected address when ConnectivityState is - // Ready. Otherwise, it is indeterminate. - connectedAddress resolver.Address -} - -// connectedAddress returns the connected address for a SubConnState. The -// address is only valid if the state is READY. -func connectedAddress(scs SubConnState) resolver.Address { - return scs.connectedAddress -} - -// setConnectedAddress sets the connected address for a SubConnState. -func setConnectedAddress(scs *SubConnState, addr resolver.Address) { - scs.connectedAddress = addr } // A Producer is a type shared among potentially many consumers. It is diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer_wrapper.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer_wrapper.go index 2c760e623f..a1e56a3893 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer_wrapper.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/balancer_wrapper.go @@ -36,7 +36,6 @@ import ( ) var ( - setConnectedAddress = internal.SetConnectedAddress.(func(*balancer.SubConnState, resolver.Address)) // noOpRegisterHealthListenerFn is used when client side health checking is // disabled. It sends a single READY update on the registered listener. noOpRegisterHealthListenerFn = func(_ context.Context, listener func(balancer.SubConnState)) func() { @@ -305,7 +304,7 @@ func newHealthData(s connectivity.State) *healthData { // updateState is invoked by grpc to push a subConn state update to the // underlying balancer. -func (acbw *acBalancerWrapper) updateState(s connectivity.State, curAddr resolver.Address, err error) { +func (acbw *acBalancerWrapper) updateState(s connectivity.State, err error) { acbw.ccb.serializer.TrySchedule(func(ctx context.Context) { if ctx.Err() != nil || acbw.ccb.balancer == nil { return @@ -317,9 +316,6 @@ func (acbw *acBalancerWrapper) updateState(s connectivity.State, curAddr resolve // opts.StateListener is set, so this cannot ever be nil. // TODO: delete this comment when UpdateSubConnState is removed. scs := balancer.SubConnState{ConnectivityState: s, ConnectionError: err} - if s == connectivity.Ready { - setConnectedAddress(&scs, curAddr) - } // Invalidate the health listener by updating the healthData. acbw.healthMu.Lock() // A race may occur if a health listener is registered soon after the diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/clientconn.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/clientconn.go index c0c2c9a76a..5dec2dacc0 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/clientconn.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/clientconn.go @@ -35,6 +35,8 @@ import ( "google.golang.org/grpc/balancer/pickfirst" "google.golang.org/grpc/codes" "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/credentials" + expstats "google.golang.org/grpc/experimental/stats" "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/grpcsync" @@ -98,6 +100,41 @@ var ( errTransportCredentialsMissing = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportCredentials() to set)") ) +var ( + disconnectionsMetric = expstats.RegisterInt64Count(expstats.MetricDescriptor{ + Name: "grpc.subchannel.disconnections", + Description: "EXPERIMENTAL. Number of times the selected subchannel becomes disconnected.", + Unit: "{disconnection}", + Labels: []string{"grpc.target"}, + OptionalLabels: []string{"grpc.lb.backend_service", "grpc.lb.locality", "grpc.disconnect_error"}, + Default: false, + }) + connectionAttemptsSucceededMetric = expstats.RegisterInt64Count(expstats.MetricDescriptor{ + Name: "grpc.subchannel.connection_attempts_succeeded", + Description: "EXPERIMENTAL. Number of successful connection attempts.", + Unit: "{attempt}", + Labels: []string{"grpc.target"}, + OptionalLabels: []string{"grpc.lb.backend_service", "grpc.lb.locality"}, + Default: false, + }) + connectionAttemptsFailedMetric = expstats.RegisterInt64Count(expstats.MetricDescriptor{ + Name: "grpc.subchannel.connection_attempts_failed", + Description: "EXPERIMENTAL. Number of failed connection attempts.", + Unit: "{attempt}", + Labels: []string{"grpc.target"}, + OptionalLabels: []string{"grpc.lb.backend_service", "grpc.lb.locality"}, + Default: false, + }) + openConnectionsMetric = expstats.RegisterInt64UpDownCount(expstats.MetricDescriptor{ + Name: "grpc.subchannel.open_connections", + Description: "EXPERIMENTAL. Number of open connections.", + Unit: "{attempt}", + Labels: []string{"grpc.target"}, + OptionalLabels: []string{"grpc.lb.backend_service", "grpc.security_level", "grpc.lb.locality"}, + Default: false, + }) +) + const ( defaultClientMaxReceiveMessageSize = 1024 * 1024 * 4 defaultClientMaxSendMessageSize = math.MaxInt32 @@ -262,9 +299,10 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * }() // This creates the name resolver, load balancer, etc. - if err := cc.idlenessMgr.ExitIdleMode(); err != nil { - return nil, err + if err := cc.exitIdleMode(); err != nil { + return nil, fmt.Errorf("failed to exit idle mode: %w", err) } + cc.idlenessMgr.UnsafeSetNotIdle() // Return now for non-blocking dials. if !cc.dopts.block { @@ -332,7 +370,7 @@ func (cc *ClientConn) addTraceEvent(msg string) { Severity: channelz.CtInfo, } } - channelz.AddTraceEvent(logger, cc.channelz, 0, ted) + channelz.AddTraceEvent(logger, cc.channelz, 1, ted) } type idler ClientConn @@ -341,14 +379,17 @@ func (i *idler) EnterIdleMode() { (*ClientConn)(i).enterIdleMode() } -func (i *idler) ExitIdleMode() error { - return (*ClientConn)(i).exitIdleMode() +func (i *idler) ExitIdleMode() { + // Ignore the error returned from this method, because from the perspective + // of the caller (idleness manager), the channel would have always moved out + // of IDLE by the time this method returns. + (*ClientConn)(i).exitIdleMode() } // exitIdleMode moves the channel out of idle mode by recreating the name // resolver and load balancer. This should never be called directly; use // cc.idlenessMgr.ExitIdleMode instead. -func (cc *ClientConn) exitIdleMode() (err error) { +func (cc *ClientConn) exitIdleMode() error { cc.mu.Lock() if cc.conns == nil { cc.mu.Unlock() @@ -356,11 +397,23 @@ func (cc *ClientConn) exitIdleMode() (err error) { } cc.mu.Unlock() + // Set state to CONNECTING before building the name resolver + // so the channel does not remain in IDLE. + cc.csMgr.updateState(connectivity.Connecting) + // This needs to be called without cc.mu because this builds a new resolver // which might update state or report error inline, which would then need to // acquire cc.mu. if err := cc.resolverWrapper.start(); err != nil { - return err + // If resolver creation fails, treat it like an error reported by the + // resolver before any valid updates. Set channel's state to + // TransientFailure, and set an erroring picker with the resolver build + // error, which will returned as part of any subsequent RPCs. + logger.Warningf("Failed to start resolver: %v", err) + cc.csMgr.updateState(connectivity.TransientFailure) + cc.mu.Lock() + cc.updateResolverStateAndUnlock(resolver.State{}, err) + return fmt.Errorf("failed to start resolver: %w", err) } cc.addTraceEvent("exiting idle mode") @@ -681,10 +734,8 @@ func (cc *ClientConn) GetState() connectivity.State { // Notice: This API is EXPERIMENTAL and may be changed or removed in a later // release. func (cc *ClientConn) Connect() { - if err := cc.idlenessMgr.ExitIdleMode(); err != nil { - cc.addTraceEvent(err.Error()) - return - } + cc.idlenessMgr.ExitIdleMode() + // If the ClientConn was not in idle mode, we need to call ExitIdle on the // LB policy so that connections can be created. cc.mu.Lock() @@ -735,8 +786,8 @@ func init() { internal.EnterIdleModeForTesting = func(cc *ClientConn) { cc.idlenessMgr.EnterIdleModeForTesting() } - internal.ExitIdleModeForTesting = func(cc *ClientConn) error { - return cc.idlenessMgr.ExitIdleMode() + internal.ExitIdleModeForTesting = func(cc *ClientConn) { + cc.idlenessMgr.ExitIdleMode() } } @@ -861,6 +912,7 @@ func (cc *ClientConn) newAddrConnLocked(addrs []resolver.Address, opts balancer. channelz: channelz.RegisterSubChannel(cc.channelz, ""), resetBackoff: make(chan struct{}), } + ac.updateTelemetryLabelsLocked() ac.ctx, ac.cancel = context.WithCancel(cc.ctx) // Start with our address set to the first address; this may be updated if // we connect to different addresses. @@ -925,25 +977,24 @@ func (cc *ClientConn) incrCallsFailed() { // connect starts creating a transport. // It does nothing if the ac is not IDLE. // TODO(bar) Move this to the addrConn section. -func (ac *addrConn) connect() error { +func (ac *addrConn) connect() { ac.mu.Lock() if ac.state == connectivity.Shutdown { if logger.V(2) { logger.Infof("connect called on shutdown addrConn; ignoring.") } ac.mu.Unlock() - return errConnClosing + return } if ac.state != connectivity.Idle { if logger.V(2) { logger.Infof("connect called on addrConn in non-idle state (%v); ignoring.", ac.state) } ac.mu.Unlock() - return nil + return } ac.resetTransportAndUnlock() - return nil } // equalAddressIgnoringBalAttributes returns true is a and b are considered equal. @@ -977,7 +1028,7 @@ func (ac *addrConn) updateAddrs(addrs []resolver.Address) { } ac.addrs = addrs - + ac.updateTelemetryLabelsLocked() if ac.state == connectivity.Shutdown || ac.state == connectivity.TransientFailure || ac.state == connectivity.Idle { @@ -1216,6 +1267,9 @@ type addrConn struct { resetBackoff chan struct{} channelz *channelz.SubChannel + + localityLabel string + backendServiceLabel string } // Note: this requires a lock on ac.mu. @@ -1223,6 +1277,18 @@ func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error) if ac.state == s { return } + + // If we are transitioning out of Ready, it means there is a disconnection. + // A SubConn can also transition from CONNECTING directly to IDLE when + // a transport is successfully created, but the connection fails + // before the SubConn can send the notification for READY. We treat + // this as a successful connection and transition to IDLE. + // TODO: https://github.com/grpc/grpc-go/issues/7862 - Remove the second + // part of the if condition below once the issue is fixed. + if ac.state == connectivity.Ready || (ac.state == connectivity.Connecting && s == connectivity.Idle) { + disconnectionsMetric.Record(ac.cc.metricsRecorderList, 1, ac.cc.target, ac.backendServiceLabel, ac.localityLabel, "unknown") + openConnectionsMetric.Record(ac.cc.metricsRecorderList, -1, ac.cc.target, ac.backendServiceLabel, ac.securityLevelLocked(), ac.localityLabel) + } ac.state = s ac.channelz.ChannelMetrics.State.Store(&s) if lastErr == nil { @@ -1230,7 +1296,7 @@ func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error) } else { channelz.Infof(logger, ac.channelz, "Subchannel Connectivity change to %v, last error: %s", s, lastErr) } - ac.acbw.updateState(s, ac.curAddr, lastErr) + ac.acbw.updateState(s, lastErr) } // adjustParams updates parameters used to create transports upon @@ -1280,6 +1346,15 @@ func (ac *addrConn) resetTransportAndUnlock() { ac.mu.Unlock() if err := ac.tryAllAddrs(acCtx, addrs, connectDeadline); err != nil { + if !errors.Is(err, context.Canceled) { + connectionAttemptsFailedMetric.Record(ac.cc.metricsRecorderList, 1, ac.cc.target, ac.backendServiceLabel, ac.localityLabel) + } else { + if logger.V(2) { + // This records cancelled connection attempts which can be later + // replaced by a metric. + logger.Infof("Context cancellation detected; not recording this as a failed connection attempt.") + } + } // TODO: #7534 - Move re-resolution requests into the pick_first LB policy // to ensure one resolution request per pass instead of per subconn failure. ac.cc.resolveNow(resolver.ResolveNowOptions{}) @@ -1319,10 +1394,50 @@ func (ac *addrConn) resetTransportAndUnlock() { } // Success; reset backoff. ac.mu.Lock() + connectionAttemptsSucceededMetric.Record(ac.cc.metricsRecorderList, 1, ac.cc.target, ac.backendServiceLabel, ac.localityLabel) + openConnectionsMetric.Record(ac.cc.metricsRecorderList, 1, ac.cc.target, ac.backendServiceLabel, ac.securityLevelLocked(), ac.localityLabel) ac.backoffIdx = 0 ac.mu.Unlock() } +// updateTelemetryLabelsLocked calculates and caches the telemetry labels based on the +// first address in addrConn. +func (ac *addrConn) updateTelemetryLabelsLocked() { + labelsFunc, ok := internal.AddressToTelemetryLabels.(func(resolver.Address) map[string]string) + if !ok || len(ac.addrs) == 0 { + // Reset defaults + ac.localityLabel = "" + ac.backendServiceLabel = "" + return + } + labels := labelsFunc(ac.addrs[0]) + ac.localityLabel = labels["grpc.lb.locality"] + ac.backendServiceLabel = labels["grpc.lb.backend_service"] +} + +type securityLevelKey struct{} + +func (ac *addrConn) securityLevelLocked() string { + var secLevel string + // During disconnection, ac.transport is nil. Fall back to the security level + // stored in the current address during connection. + if ac.transport == nil { + secLevel, _ = ac.curAddr.Attributes.Value(securityLevelKey{}).(string) + return secLevel + } + authInfo := ac.transport.Peer().AuthInfo + if ci, ok := authInfo.(interface { + GetCommonAuthInfo() credentials.CommonAuthInfo + }); ok { + secLevel = ci.GetCommonAuthInfo().SecurityLevel.String() + // Store the security level in the current address' attributes so + // that it remains available for disconnection metrics after the + // transport is closed. + ac.curAddr.Attributes = ac.curAddr.Attributes.WithValue(securityLevelKey{}, secLevel) + } + return secLevel +} + // tryAllAddrs tries to create a connection to the addresses, and stop when at // the first successful one. It returns an error if no address was successfully // connected, or updates ac appropriately with the new transport. @@ -1412,25 +1527,26 @@ func (ac *addrConn) createTransport(ctx context.Context, addr resolver.Address, } ac.mu.Lock() - defer ac.mu.Unlock() if ctx.Err() != nil { // This can happen if the subConn was removed while in `Connecting` // state. tearDown() would have set the state to `Shutdown`, but // would not have closed the transport since ac.transport would not // have been set at that point. - // - // We run this in a goroutine because newTr.Close() calls onClose() + + // We unlock ac.mu because newTr.Close() calls onClose() // inline, which requires locking ac.mu. - // + ac.mu.Unlock() + // The error we pass to Close() is immaterial since there are no open // streams at this point, so no trailers with error details will be sent // out. We just need to pass a non-nil error. // // This can also happen when updateAddrs is called during a connection // attempt. - go newTr.Close(transport.ErrConnClosing) + newTr.Close(transport.ErrConnClosing) return nil } + defer ac.mu.Unlock() if hctx.Err() != nil { // onClose was already called for this connection, but the connection // was successfully established first. Consider it a success and set diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/credentials/tls.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/credentials/tls.go index 8277be7d6f..0bcd16dbbf 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/credentials/tls.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/credentials/tls.go @@ -56,9 +56,13 @@ func (t TLSInfo) AuthType() string { // non-nil error if the validation fails. func (t TLSInfo) ValidateAuthority(authority string) error { var errs []error + host, _, err := net.SplitHostPort(authority) + if err != nil { + host = authority + } for _, cert := range t.State.PeerCertificates { var err error - if err = cert.VerifyHostname(authority); err == nil { + if err = cert.VerifyHostname(host); err == nil { return nil } errs = append(errs, err) diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/encoding/encoding.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/encoding/encoding.go index dadd21e40f..296f38c3a8 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/encoding/encoding.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/encoding/encoding.go @@ -58,10 +58,6 @@ func init() { // Compressor is used for compressing and decompressing when sending or // receiving messages. -// -// If a Compressor implements `DecompressedSize(compressedBytes []byte) int`, -// gRPC will invoke it to determine the size of the buffer allocated for the -// result of decompression. A return value of -1 indicates unknown size. type Compressor interface { // Compress writes the data written to wc to w after compressing it. If an // error occurs while initializing the compressor, that error is returned diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/encoding/gzip/gzip.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/encoding/gzip/gzip.go index 6306e8bb0f..153e4dbfbf 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/encoding/gzip/gzip.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/encoding/gzip/gzip.go @@ -27,7 +27,6 @@ package gzip import ( "compress/gzip" - "encoding/binary" "fmt" "io" "sync" @@ -111,17 +110,6 @@ func (z *reader) Read(p []byte) (n int, err error) { return n, err } -// RFC1952 specifies that the last four bytes "contains the size of -// the original (uncompressed) input data modulo 2^32." -// gRPC has a max message size of 2GB so we don't need to worry about wraparound. -func (c *compressor) DecompressedSize(buf []byte) int { - last := len(buf) - if last < 4 { - return -1 - } - return int(binary.LittleEndian.Uint32(buf[last-4 : last])) -} - func (c *compressor) Name() string { return Name } diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/experimental/stats/metricregistry.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/experimental/stats/metricregistry.go index 2b57ba65a3..472813f58f 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/experimental/stats/metricregistry.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/experimental/stats/metricregistry.go @@ -76,6 +76,7 @@ const ( MetricTypeFloatHisto MetricTypeIntGauge MetricTypeIntUpDownCount + MetricTypeIntAsyncGauge ) // Int64CountHandle is a typed handle for a int count metric. This handle @@ -172,6 +173,30 @@ func (h *Int64GaugeHandle) Record(recorder MetricsRecorder, incr int64, labels . recorder.RecordInt64Gauge(h, incr, labels...) } +// AsyncMetric is a marker interface for asynchronous metric types. +type AsyncMetric interface { + isAsync() + Descriptor() *MetricDescriptor +} + +// Int64AsyncGaugeHandle is a typed handle for an int gauge metric. This handle is +// passed at the recording point in order to know which metric to record on. +type Int64AsyncGaugeHandle MetricDescriptor + +// isAsync implements the AsyncMetric interface. +func (h *Int64AsyncGaugeHandle) isAsync() {} + +// Descriptor returns the int64 gauge handle typecast to a pointer to a +// MetricDescriptor. +func (h *Int64AsyncGaugeHandle) Descriptor() *MetricDescriptor { + return (*MetricDescriptor)(h) +} + +// Record records the int64 gauge value on the metrics recorder provided. +func (h *Int64AsyncGaugeHandle) Record(recorder AsyncMetricsRecorder, value int64, labels ...string) { + recorder.RecordInt64AsyncGauge(h, value, labels...) +} + // registeredMetrics are the registered metric descriptor names. var registeredMetrics = make(map[string]bool) @@ -282,6 +307,20 @@ func RegisterInt64UpDownCount(descriptor MetricDescriptor) *Int64UpDownCountHand return (*Int64UpDownCountHandle)(descPtr) } +// RegisterInt64AsyncGauge registers the metric description onto the global registry. +// It returns a typed handle to use for recording data. +// +// NOTE: this function must only be called during initialization time (i.e. in +// an init() function), and is not thread-safe. If multiple metrics are +// registered with the same name, this function will panic. +func RegisterInt64AsyncGauge(descriptor MetricDescriptor) *Int64AsyncGaugeHandle { + registerMetric(descriptor.Name, descriptor.Default) + descriptor.Type = MetricTypeIntAsyncGauge + descPtr := &descriptor + metricsRegistry[descriptor.Name] = descPtr + return (*Int64AsyncGaugeHandle)(descPtr) +} + // snapshotMetricsRegistryForTesting snapshots the global data of the metrics // registry. Returns a cleanup function that sets the metrics registry to its // original state. diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/experimental/stats/metrics.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/experimental/stats/metrics.go index cb57f1a748..88742724a4 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/experimental/stats/metrics.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/experimental/stats/metrics.go @@ -19,9 +19,13 @@ // Package stats contains experimental metrics/stats API's. package stats -import "google.golang.org/grpc/stats" +import ( + "google.golang.org/grpc/internal" + "google.golang.org/grpc/stats" +) // MetricsRecorder records on metrics derived from metric registry. +// Implementors must embed UnimplementedMetricsRecorder. type MetricsRecorder interface { // RecordInt64Count records the measurement alongside labels on the int // count associated with the provided handle. @@ -41,6 +45,46 @@ type MetricsRecorder interface { // RecordInt64UpDownCounter records the measurement alongside labels on the int // count associated with the provided handle. RecordInt64UpDownCount(handle *Int64UpDownCountHandle, incr int64, labels ...string) + // RegisterAsyncReporter registers a reporter to produce metric values for + // only the listed descriptors. The returned function must be called when + // the metrics are no longer needed, which will remove the reporter. The + // returned method needs to be idempotent and concurrent safe. + RegisterAsyncReporter(reporter AsyncMetricReporter, descriptors ...AsyncMetric) func() + + // EnforceMetricsRecorderEmbedding is included to force implementers to embed + // another implementation of this interface, allowing gRPC to add methods + // without breaking users. + internal.EnforceMetricsRecorderEmbedding +} + +// AsyncMetricReporter is an interface for types that record metrics asynchronously +// for the set of descriptors they are registered with. The AsyncMetricsRecorder +// parameter is used to record values for these metrics. +// +// Implementations must make unique recordings across all registered +// AsyncMetricReporters. Meaning, they should not report values for a metric with +// the same attributes as another AsyncMetricReporter will report. +// +// Implementations must be concurrent-safe. +type AsyncMetricReporter interface { + // Report records metric values using the provided recorder. + Report(AsyncMetricsRecorder) error +} + +// AsyncMetricReporterFunc is an adapter to allow the use of ordinary functions as +// AsyncMetricReporters. +type AsyncMetricReporterFunc func(AsyncMetricsRecorder) error + +// Report calls f(r). +func (f AsyncMetricReporterFunc) Report(r AsyncMetricsRecorder) error { + return f(r) +} + +// AsyncMetricsRecorder records on asynchronous metrics derived from metric registry. +type AsyncMetricsRecorder interface { + // RecordInt64AsyncGauge records the measurement alongside labels on the int + // count associated with the provided handle asynchronously + RecordInt64AsyncGauge(handle *Int64AsyncGaugeHandle, incr int64, labels ...string) } // Metrics is an experimental legacy alias of the now-stable stats.MetricSet. @@ -55,3 +99,33 @@ type Metric = string func NewMetrics(metrics ...Metric) *Metrics { return stats.NewMetricSet(metrics...) } + +// UnimplementedMetricsRecorder must be embedded to have forward compatible implementations. +type UnimplementedMetricsRecorder struct { + internal.EnforceMetricsRecorderEmbedding +} + +// RecordInt64Count provides a no-op implementation. +func (UnimplementedMetricsRecorder) RecordInt64Count(*Int64CountHandle, int64, ...string) {} + +// RecordFloat64Count provides a no-op implementation. +func (UnimplementedMetricsRecorder) RecordFloat64Count(*Float64CountHandle, float64, ...string) {} + +// RecordInt64Histo provides a no-op implementation. +func (UnimplementedMetricsRecorder) RecordInt64Histo(*Int64HistoHandle, int64, ...string) {} + +// RecordFloat64Histo provides a no-op implementation. +func (UnimplementedMetricsRecorder) RecordFloat64Histo(*Float64HistoHandle, float64, ...string) {} + +// RecordInt64Gauge provides a no-op implementation. +func (UnimplementedMetricsRecorder) RecordInt64Gauge(*Int64GaugeHandle, int64, ...string) {} + +// RecordInt64UpDownCount provides a no-op implementation. +func (UnimplementedMetricsRecorder) RecordInt64UpDownCount(*Int64UpDownCountHandle, int64, ...string) { +} + +// RegisterAsyncReporter provides a no-op implementation. +func (UnimplementedMetricsRecorder) RegisterAsyncReporter(AsyncMetricReporter, ...AsyncMetric) func() { + // No-op: Return an empty function to ensure caller doesn't panic on nil function call + return func() {} +} diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go index f2c01f296a..e99cd5c838 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 +// - protoc-gen-go-grpc v1.6.0 // - protoc v5.27.1 // source: grpc/health/v1/health.proto diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/interceptor.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/interceptor.go index 877d78fc3d..099e3d0933 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/interceptor.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/interceptor.go @@ -97,8 +97,12 @@ type StreamServerInfo struct { IsServerStream bool } -// StreamServerInterceptor provides a hook to intercept the execution of a streaming RPC on the server. -// info contains all the information of this RPC the interceptor can operate on. And handler is the -// service method implementation. It is the responsibility of the interceptor to invoke handler to -// complete the RPC. +// StreamServerInterceptor provides a hook to intercept the execution of a +// streaming RPC on the server. +// +// srv is the service implementation on which the RPC was invoked, and needs to +// be passed to handler, and not used otherwise. ss is the server side of the +// stream. info contains all the information of this RPC the interceptor can +// operate on. And handler is the service method implementation. It is the +// responsibility of the interceptor to invoke handler to complete the RPC. type StreamServerInterceptor func(srv any, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go index ba25b89887..f38de74a49 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go @@ -67,6 +67,10 @@ type Balancer struct { // balancerCurrent before the UpdateSubConnState is called on the // balancerCurrent. currentMu sync.Mutex + + // activeGoroutines tracks all the goroutines that this balancer has started + // and that should be waited on when the balancer closes. + activeGoroutines sync.WaitGroup } // swap swaps out the current lb with the pending lb and updates the ClientConn. @@ -76,7 +80,9 @@ func (gsb *Balancer) swap() { cur := gsb.balancerCurrent gsb.balancerCurrent = gsb.balancerPending gsb.balancerPending = nil + gsb.activeGoroutines.Add(1) go func() { + defer gsb.activeGoroutines.Done() gsb.currentMu.Lock() defer gsb.currentMu.Unlock() cur.Close() @@ -274,6 +280,7 @@ func (gsb *Balancer) Close() { currentBalancerToClose.Close() pendingBalancerToClose.Close() + gsb.activeGoroutines.Wait() } // balancerWrapper wraps a balancer.Balancer, and overrides some Balancer @@ -324,7 +331,12 @@ func (bw *balancerWrapper) UpdateState(state balancer.State) { defer bw.gsb.mu.Unlock() bw.lastState = state + // If Close() acquires the mutex before UpdateState(), the balancer + // will already have been removed from the current or pending state when + // reaching this point. if !bw.gsb.balancerCurrentOrPending(bw) { + // Returning here ensures that (*Balancer).swap() is not invoked after + // (*Balancer).Close() and therefore prevents "use after close". return } diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/balancer/weight/weight.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/balancer/weight/weight.go new file mode 100644 index 0000000000..11beb07d14 --- /dev/null +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/balancer/weight/weight.go @@ -0,0 +1,66 @@ +/* + * + * Copyright 2025 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Package weight contains utilities to manage endpoint weights. Weights are +// used by LB policies such as ringhash to distribute load across multiple +// endpoints. +package weight + +import ( + "fmt" + + "google.golang.org/grpc/resolver" +) + +// attributeKey is the type used as the key to store EndpointInfo in the +// Attributes field of resolver.Endpoint. +type attributeKey struct{} + +// EndpointInfo will be stored in the Attributes field of Endpoints in order to +// use the ringhash balancer. +type EndpointInfo struct { + Weight uint32 +} + +// Equal allows the values to be compared by Attributes.Equal. +func (a EndpointInfo) Equal(o any) bool { + oa, ok := o.(EndpointInfo) + return ok && oa.Weight == a.Weight +} + +// Set returns a copy of endpoint in which the Attributes field is updated with +// EndpointInfo. +func Set(endpoint resolver.Endpoint, epInfo EndpointInfo) resolver.Endpoint { + endpoint.Attributes = endpoint.Attributes.WithValue(attributeKey{}, epInfo) + return endpoint +} + +// String returns a human-readable representation of EndpointInfo. +// This method is intended for logging, testing, and debugging purposes only. +// Do not rely on the output format, as it is not guaranteed to remain stable. +func (a EndpointInfo) String() string { + return fmt.Sprintf("Weight: %d", a.Weight) +} + +// FromEndpoint returns the EndpointInfo stored in the Attributes field of an +// endpoint. It returns an empty EndpointInfo if attribute is not found. +func FromEndpoint(endpoint resolver.Endpoint) EndpointInfo { + v := endpoint.Attributes.Value(attributeKey{}) + ei, _ := v.(EndpointInfo) + return ei +} diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go index 91f760936c..7ad6fb44ca 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go @@ -77,6 +77,33 @@ var ( // - Target resolution is disabled. // - The DNS resolver is being used. EnableDefaultPortForProxyTarget = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_DEFAULT_PORT_FOR_PROXY_TARGET", true) + + // XDSAuthorityRewrite indicates whether xDS authority rewriting is enabled. + // This feature is defined in gRFC A81 and is enabled by setting the + // environment variable GRPC_EXPERIMENTAL_XDS_AUTHORITY_REWRITE to "true". + XDSAuthorityRewrite = boolFromEnv("GRPC_EXPERIMENTAL_XDS_AUTHORITY_REWRITE", false) + + // PickFirstWeightedShuffling indicates whether weighted endpoint shuffling + // is enabled in the pick_first LB policy, as defined in gRFC A113. This + // feature can be disabled by setting the environment variable + // GRPC_EXPERIMENTAL_PF_WEIGHTED_SHUFFLING to "false". + PickFirstWeightedShuffling = boolFromEnv("GRPC_EXPERIMENTAL_PF_WEIGHTED_SHUFFLING", true) + + // DisableStrictPathChecking indicates whether strict path checking is + // disabled. This feature can be disabled by setting the environment + // variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to "true". + // + // When strict path checking is enabled, gRPC will reject requests with + // paths that do not conform to the gRPC over HTTP/2 specification found at + // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md. + // + // When disabled, gRPC will allow paths that do not contain a leading slash. + // Enabling strict path checking is recommended for security reasons, as it + // prevents potential path traversal vulnerabilities. + // + // A future release will remove this environment variable, enabling strict + // path checking behavior unconditionally. + DisableStrictPathChecking = boolFromEnv("GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING", false) ) func boolFromEnv(envVar string, def bool) bool { diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/experimental.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/experimental.go index 7617be2158..8a999917d9 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/experimental.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/experimental.go @@ -25,4 +25,11 @@ var ( // BufferPool is implemented by the grpc package and returns a server // option to configure a shared buffer pool for a grpc.Server. BufferPool any // func (grpc.SharedBufferPool) grpc.ServerOption + + // SetDefaultBufferPool updates the default buffer pool. + SetDefaultBufferPool any // func(mem.BufferPool) + + // AcceptCompressors is implemented by the grpc package and returns + // a call option that restricts the grpc-accept-encoding header for a call. + AcceptCompressors any // func(...string) grpc.CallOption ) diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/idle/idle.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/idle/idle.go index 2c13ee9dac..d3cd24f80b 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/idle/idle.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/idle/idle.go @@ -21,7 +21,6 @@ package idle import ( - "fmt" "math" "sync" "sync/atomic" @@ -33,15 +32,15 @@ var timeAfterFunc = func(d time.Duration, f func()) *time.Timer { return time.AfterFunc(d, f) } -// Enforcer is the functionality provided by grpc.ClientConn to enter -// and exit from idle mode. -type Enforcer interface { - ExitIdleMode() error +// ClientConn is the functionality provided by grpc.ClientConn to enter and exit +// from idle mode. +type ClientConn interface { + ExitIdleMode() EnterIdleMode() } -// Manager implements idleness detection and calls the configured Enforcer to -// enter/exit idle mode when appropriate. Must be created by NewManager. +// Manager implements idleness detection and calls the ClientConn to enter/exit +// idle mode when appropriate. Must be created by NewManager. type Manager struct { // State accessed atomically. lastCallEndTime int64 // Unix timestamp in nanos; time when the most recent RPC completed. @@ -51,8 +50,8 @@ type Manager struct { // Can be accessed without atomics or mutex since these are set at creation // time and read-only after that. - enforcer Enforcer // Functionality provided by grpc.ClientConn. - timeout time.Duration + cc ClientConn // Functionality provided by grpc.ClientConn. + timeout time.Duration // idleMu is used to guarantee mutual exclusion in two scenarios: // - Opposing intentions: @@ -72,9 +71,9 @@ type Manager struct { // NewManager creates a new idleness manager implementation for the // given idle timeout. It begins in idle mode. -func NewManager(enforcer Enforcer, timeout time.Duration) *Manager { +func NewManager(cc ClientConn, timeout time.Duration) *Manager { return &Manager{ - enforcer: enforcer, + cc: cc, timeout: timeout, actuallyIdle: true, activeCallsCount: -math.MaxInt32, @@ -127,7 +126,7 @@ func (m *Manager) handleIdleTimeout() { // Now that we've checked that there has been no activity, attempt to enter // idle mode, which is very likely to succeed. - if m.tryEnterIdleMode() { + if m.tryEnterIdleMode(true) { // Successfully entered idle mode. No timer needed until we exit idle. return } @@ -142,10 +141,13 @@ func (m *Manager) handleIdleTimeout() { // that, it performs a last minute check to ensure that no new RPC has come in, // making the channel active. // +// checkActivity controls if a check for RPC activity, since the last time the +// idle_timeout fired, is made. + // Return value indicates whether or not the channel moved to idle mode. // // Holds idleMu which ensures mutual exclusion with exitIdleMode. -func (m *Manager) tryEnterIdleMode() bool { +func (m *Manager) tryEnterIdleMode(checkActivity bool) bool { // Setting the activeCallsCount to -math.MaxInt32 indicates to OnCallBegin() // that the channel is either in idle mode or is trying to get there. if !atomic.CompareAndSwapInt32(&m.activeCallsCount, 0, -math.MaxInt32) { @@ -166,7 +168,7 @@ func (m *Manager) tryEnterIdleMode() bool { atomic.AddInt32(&m.activeCallsCount, math.MaxInt32) return false } - if atomic.LoadInt32(&m.activeSinceLastTimerCheck) == 1 { + if checkActivity && atomic.LoadInt32(&m.activeSinceLastTimerCheck) == 1 { // A very short RPC could have come in (and also finished) after we // checked for calls count and activity in handleIdleTimeout(), but // before the CAS operation. So, we need to check for activity again. @@ -177,44 +179,37 @@ func (m *Manager) tryEnterIdleMode() bool { // No new RPCs have come in since we set the active calls count value to // -math.MaxInt32. And since we have the lock, it is safe to enter idle mode // unconditionally now. - m.enforcer.EnterIdleMode() + m.cc.EnterIdleMode() m.actuallyIdle = true return true } // EnterIdleModeForTesting instructs the channel to enter idle mode. func (m *Manager) EnterIdleModeForTesting() { - m.tryEnterIdleMode() + m.tryEnterIdleMode(false) } // OnCallBegin is invoked at the start of every RPC. -func (m *Manager) OnCallBegin() error { +func (m *Manager) OnCallBegin() { if m.isClosed() { - return nil + return } if atomic.AddInt32(&m.activeCallsCount, 1) > 0 { // Channel is not idle now. Set the activity bit and allow the call. atomic.StoreInt32(&m.activeSinceLastTimerCheck, 1) - return nil + return } // Channel is either in idle mode or is in the process of moving to idle // mode. Attempt to exit idle mode to allow this RPC. - if err := m.ExitIdleMode(); err != nil { - // Undo the increment to calls count, and return an error causing the - // RPC to fail. - atomic.AddInt32(&m.activeCallsCount, -1) - return err - } - + m.ExitIdleMode() atomic.StoreInt32(&m.activeSinceLastTimerCheck, 1) - return nil } -// ExitIdleMode instructs m to call the enforcer's ExitIdleMode and update m's +// ExitIdleMode instructs m to call the ClientConn's ExitIdleMode and update its // internal state. -func (m *Manager) ExitIdleMode() error { +func (m *Manager) ExitIdleMode() { // Holds idleMu which ensures mutual exclusion with tryEnterIdleMode. m.idleMu.Lock() defer m.idleMu.Unlock() @@ -231,12 +226,10 @@ func (m *Manager) ExitIdleMode() error { // m.ExitIdleMode. // // In any case, there is nothing to do here. - return nil + return } - if err := m.enforcer.ExitIdleMode(); err != nil { - return fmt.Errorf("failed to exit idle mode: %w", err) - } + m.cc.ExitIdleMode() // Undo the idle entry process. This also respects any new RPC attempts. atomic.AddInt32(&m.activeCallsCount, math.MaxInt32) @@ -244,7 +237,23 @@ func (m *Manager) ExitIdleMode() error { // Start a new timer to fire after the configured idle timeout. m.resetIdleTimerLocked(m.timeout) - return nil +} + +// UnsafeSetNotIdle instructs the Manager to update its internal state to +// reflect the reality that the channel is no longer in IDLE mode. +// +// N.B. This method is intended only for internal use by the gRPC client +// when it exits IDLE mode **manually** from `Dial`. The callsite must ensure: +// - The channel was **actually in IDLE mode** immediately prior to the call. +// - There is **no concurrent activity** that could cause the channel to exit +// IDLE mode *naturally* at the same time. +func (m *Manager) UnsafeSetNotIdle() { + m.idleMu.Lock() + defer m.idleMu.Unlock() + + atomic.AddInt32(&m.activeCallsCount, math.MaxInt32) + m.actuallyIdle = false + m.resetIdleTimerLocked(m.timeout) } // OnCallEnd is invoked at the end of every RPC. diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/internal.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/internal.go index 2699223a27..4b3d563f8d 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/internal.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/internal.go @@ -211,22 +211,11 @@ var ( // default resolver scheme. UserSetDefaultScheme = false - // ConnectedAddress returns the connected address for a SubConnState. The - // address is only valid if the state is READY. - ConnectedAddress any // func (scs SubConnState) resolver.Address - - // SetConnectedAddress sets the connected address for a SubConnState. - SetConnectedAddress any // func(scs *SubConnState, addr resolver.Address) - // SnapshotMetricRegistryForTesting snapshots the global data of the metric // registry. Returns a cleanup function that sets the metric registry to its // original state. Only called in testing functions. SnapshotMetricRegistryForTesting func() func() - // SetDefaultBufferPoolForTesting updates the default buffer pool, for - // testing purposes. - SetDefaultBufferPoolForTesting any // func(mem.BufferPool) - // SetBufferPoolingThresholdForTesting updates the buffer pooling threshold, for // testing purposes. SetBufferPoolingThresholdForTesting any // func(int) @@ -244,6 +233,18 @@ var ( // When set, the function will be called before the stream enters // the blocking state. NewStreamWaitingForResolver = func() {} + + // AddressToTelemetryLabels is an xDS-provided function to extract telemetry + // labels from a resolver.Address. Callers must assert its type before calling. + AddressToTelemetryLabels any // func(addr resolver.Address) map[string]string + + // AsyncReporterCleanupDelegate is initialized to a pass-through function by + // default (production behavior), allowing tests to swap it with an + // implementation which tracks registration of async reporter and its + // corresponding cleanup. + AsyncReporterCleanupDelegate = func(cleanup func()) func() { + return cleanup + } ) // HealthChecker defines the signature of the client-side LB channel health @@ -291,3 +292,9 @@ type EnforceClientConnEmbedding interface { type Timer interface { Stop() bool } + +// EnforceMetricsRecorderEmbedding is used to enforce proper MetricsRecorder +// implementation embedding. +type EnforceMetricsRecorderEmbedding interface { + enforceMetricsRecorderEmbedding() +} diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go index ada5251cff..70b89e4d7f 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go @@ -125,7 +125,10 @@ func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts // IP address. if ipAddr, err := formatIP(host); err == nil { addr := []resolver.Address{{Addr: ipAddr + ":" + port}} - cc.UpdateState(resolver.State{Addresses: addr}) + cc.UpdateState(resolver.State{ + Addresses: addr, + Endpoints: []resolver.Endpoint{{Addresses: addr}}, + }) return deadResolver{}, nil } @@ -342,7 +345,15 @@ func (d *dnsResolver) lookup() (*resolver.State, error) { return nil, hostErr } - state := resolver.State{Addresses: addrs} + eps := make([]resolver.Endpoint, 0, len(addrs)) + for _, addr := range addrs { + eps = append(eps, resolver.Endpoint{Addresses: []resolver.Address{addr}}) + } + + state := resolver.State{ + Addresses: addrs, + Endpoints: eps, + } if len(srv) > 0 { state = grpclbstate.Set(state, &grpclbstate.State{BalancerAddresses: srv}) } diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/stats/metrics_recorder_list.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/stats/metrics_recorder_list.go index d5f7e4d62d..1c8c2ab303 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/stats/metrics_recorder_list.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/stats/metrics_recorder_list.go @@ -20,6 +20,7 @@ import ( "fmt" estats "google.golang.org/grpc/experimental/stats" + "google.golang.org/grpc/internal" "google.golang.org/grpc/stats" ) @@ -28,6 +29,7 @@ import ( // It eats any record calls where the label values provided do not match the // number of label keys. type MetricsRecorderList struct { + internal.EnforceMetricsRecorderEmbedding // metricsRecorders are the metrics recorders this list will forward to. metricsRecorders []estats.MetricsRecorder } @@ -113,3 +115,61 @@ func (l *MetricsRecorderList) RecordInt64Gauge(handle *estats.Int64GaugeHandle, metricRecorder.RecordInt64Gauge(handle, incr, labels...) } } + +// RegisterAsyncReporter forwards the registration to all underlying metrics +// recorders. +// +// It returns a cleanup function that, when called, invokes the cleanup function +// returned by each underlying recorder, ensuring the reporter is unregistered +// from all of them. +func (l *MetricsRecorderList) RegisterAsyncReporter(reporter estats.AsyncMetricReporter, metrics ...estats.AsyncMetric) func() { + descriptorsMap := make(map[*estats.MetricDescriptor]bool, len(metrics)) + for _, m := range metrics { + descriptorsMap[m.Descriptor()] = true + } + unregisterFns := make([]func(), 0, len(l.metricsRecorders)) + for _, mr := range l.metricsRecorders { + // Wrap the AsyncMetricsRecorder to intercept calls to RecordInt64Gauge + // and validate the labels. + wrappedCallback := func(recorder estats.AsyncMetricsRecorder) error { + wrappedRecorder := &asyncRecorderWrapper{ + delegate: recorder, + descriptors: descriptorsMap, + } + return reporter.Report(wrappedRecorder) + } + unregisterFns = append(unregisterFns, mr.RegisterAsyncReporter(estats.AsyncMetricReporterFunc(wrappedCallback), metrics...)) + } + + // Wrap the cleanup function using the internal delegate. + // In production, this returns realCleanup as-is. + // In tests, the leak checker can swap this to track the registration lifetime. + return internal.AsyncReporterCleanupDelegate(defaultCleanUp(unregisterFns)) +} + +func defaultCleanUp(unregisterFns []func()) func() { + return func() { + for _, unregister := range unregisterFns { + unregister() + } + } +} + +type asyncRecorderWrapper struct { + delegate estats.AsyncMetricsRecorder + descriptors map[*estats.MetricDescriptor]bool +} + +// RecordIntAsync64Gauge records the measurement alongside labels on the int +// gauge associated with the provided handle. +func (w *asyncRecorderWrapper) RecordInt64AsyncGauge(handle *estats.Int64AsyncGaugeHandle, value int64, labels ...string) { + // Ensure only metrics for descriptors passed during callback registration + // are emitted. + d := handle.Descriptor() + if _, ok := w.descriptors[d]; !ok { + return + } + // Validate labels and delegate. + verifyLabels(d, labels...) + w.delegate.RecordInt64AsyncGauge(handle, value, labels...) +} diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/client_stream.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/client_stream.go index 980452519e..cd8152ef13 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/client_stream.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/client_stream.go @@ -24,6 +24,7 @@ import ( "golang.org/x/net/http2" "google.golang.org/grpc/mem" "google.golang.org/grpc/metadata" + "google.golang.org/grpc/stats" "google.golang.org/grpc/status" ) @@ -46,10 +47,11 @@ type ClientStream struct { // meaningful after headerChan is closed (always call waitOnHeader() before // reading its value). headerValid bool - noHeaders bool // set if the client never received headers (set only after the stream is done). - headerChanClosed uint32 // set when headerChan is closed. Used to avoid closing headerChan multiple times. - bytesReceived atomic.Bool // indicates whether any bytes have been received on this stream - unprocessed atomic.Bool // set if the server sends a refused stream or GOAWAY including this stream + noHeaders bool // set if the client never received headers (set only after the stream is done). + headerChanClosed uint32 // set when headerChan is closed. Used to avoid closing headerChan multiple times. + bytesReceived atomic.Bool // indicates whether any bytes have been received on this stream + unprocessed atomic.Bool // set if the server sends a refused stream or GOAWAY including this stream + statsHandler stats.Handler // nil for internal streams (e.g., health check, ORCA) where telemetry is not supported. } // Read reads an n byte message from the input stream. diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/controlbuf.go index 2dcd1e63bd..7efa524785 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/controlbuf.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/controlbuf.go @@ -24,16 +24,13 @@ import ( "fmt" "net" "runtime" - "strconv" "sync" "sync/atomic" "golang.org/x/net/http2" "golang.org/x/net/http2/hpack" "google.golang.org/grpc/internal/grpclog" - "google.golang.org/grpc/internal/grpcutil" "google.golang.org/grpc/mem" - "google.golang.org/grpc/status" ) var updateHeaderTblSize = func(e *hpack.Encoder, v uint32) { @@ -147,11 +144,9 @@ type cleanupStream struct { func (c *cleanupStream) isTransportResponseFrame() bool { return c.rst } // Results in a RST_STREAM type earlyAbortStream struct { - httpStatus uint32 - streamID uint32 - contentSubtype string - status *status.Status - rst bool + streamID uint32 + rst bool + hf []hpack.HeaderField // Pre-built header fields } func (*earlyAbortStream) isTransportResponseFrame() bool { return false } @@ -843,18 +838,7 @@ func (l *loopyWriter) earlyAbortStreamHandler(eas *earlyAbortStream) error { if l.side == clientSide { return errors.New("earlyAbortStream not handled on client") } - // In case the caller forgets to set the http status, default to 200. - if eas.httpStatus == 0 { - eas.httpStatus = 200 - } - headerFields := []hpack.HeaderField{ - {Name: ":status", Value: strconv.Itoa(int(eas.httpStatus))}, - {Name: "content-type", Value: grpcutil.ContentType(eas.contentSubtype)}, - {Name: "grpc-status", Value: strconv.Itoa(int(eas.status.Code()))}, - {Name: "grpc-message", Value: encodeGrpcMessage(eas.status.Message())}, - } - - if err := l.writeHeader(eas.streamID, true, headerFields, nil); err != nil { + if err := l.writeHeader(eas.streamID, true, eas.hf, nil); err != nil { return err } if eas.rst { diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/http2_client.go index 65b4ab2439..37b1acc340 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/http2_client.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/http2_client.go @@ -370,7 +370,7 @@ func NewHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts }) t.logger = prefixLoggerForClientTransport(t) // Add peer information to the http2client context. - t.ctx = peer.NewContext(t.ctx, t.getPeer()) + t.ctx = peer.NewContext(t.ctx, t.Peer()) if md, ok := addr.Metadata.(*metadata.MD); ok { t.md = *md @@ -478,7 +478,7 @@ func NewHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts return t, nil } -func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *ClientStream { +func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr, handler stats.Handler) *ClientStream { // TODO(zhaoq): Handle uint32 overflow of Stream.id. s := &ClientStream{ Stream: Stream{ @@ -486,10 +486,11 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *ClientSt sendCompress: callHdr.SendCompress, contentSubtype: callHdr.ContentSubtype, }, - ct: t, - done: make(chan struct{}), - headerChan: make(chan struct{}), - doneFunc: callHdr.DoneFunc, + ct: t, + done: make(chan struct{}), + headerChan: make(chan struct{}), + doneFunc: callHdr.DoneFunc, + statsHandler: handler, } s.Stream.buf.init() s.Stream.wq.init(defaultWriteQuota, s.done) @@ -510,7 +511,7 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *ClientSt return s } -func (t *http2Client) getPeer() *peer.Peer { +func (t *http2Client) Peer() *peer.Peer { return &peer.Peer{ Addr: t.remoteAddr, AuthInfo: t.authInfo, // Can be nil @@ -551,6 +552,9 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr) hfLen := 7 // :method, :scheme, :path, :authority, content-type, user-agent, te hfLen += len(authData) + len(callAuthData) registeredCompressors := t.registeredCompressors + if callHdr.AcceptedCompressors != nil { + registeredCompressors = *callHdr.AcceptedCompressors + } if callHdr.PreviousAttempts > 0 { hfLen++ } @@ -741,8 +745,8 @@ func (e NewStreamError) Error() string { // NewStream creates a stream and registers it into the transport as "active" // streams. All non-nil errors returned will be *NewStreamError. -func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*ClientStream, error) { - ctx = peer.NewContext(ctx, t.getPeer()) +func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr, handler stats.Handler) (*ClientStream, error) { + ctx = peer.NewContext(ctx, t.Peer()) // ServerName field of the resolver returned address takes precedence over // Host field of CallHdr to determine the :authority header. This is because, @@ -778,7 +782,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*ClientS if err != nil { return nil, &NewStreamError{Err: err, AllowTransparentRetry: false} } - s := t.newStream(ctx, callHdr) + s := t.newStream(ctx, callHdr, handler) cleanup := func(err error) { if s.swapState(streamDone) == streamDone { // If it was already done, return. @@ -899,7 +903,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*ClientS return nil, &NewStreamError{Err: ErrConnClosing, AllowTransparentRetry: true} } } - if t.statsHandler != nil { + if s.statsHandler != nil { header, ok := metadata.FromOutgoingContext(ctx) if ok { header.Set("user-agent", t.userAgent) @@ -908,7 +912,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*ClientS } // Note: The header fields are compressed with hpack after this call returns. // No WireLength field is set here. - t.statsHandler.HandleRPC(s.ctx, &stats.OutHeader{ + s.statsHandler.HandleRPC(s.ctx, &stats.OutHeader{ Client: true, FullMethod: callHdr.Method, RemoteAddr: t.remoteAddr, @@ -1485,7 +1489,7 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { case "grpc-status": code, err := strconv.ParseInt(hf.Value, 10, 32) if err != nil { - se := status.New(codes.Internal, fmt.Sprintf("transport: malformed grpc-status: %v", err)) + se := status.New(codes.Unknown, fmt.Sprintf("transport: malformed grpc-status: %v", err)) t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream) return } @@ -1584,16 +1588,16 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { } } - if t.statsHandler != nil { + if s.statsHandler != nil { if !endStream { - t.statsHandler.HandleRPC(s.ctx, &stats.InHeader{ + s.statsHandler.HandleRPC(s.ctx, &stats.InHeader{ Client: true, WireLength: int(frame.Header().Length), Header: metadata.MD(mdata).Copy(), Compression: s.recvCompress, }) } else { - t.statsHandler.HandleRPC(s.ctx, &stats.InTrailer{ + s.statsHandler.HandleRPC(s.ctx, &stats.InTrailer{ Client: true, WireLength: int(frame.Header().Length), Trailer: metadata.MD(mdata).Copy(), @@ -1807,8 +1811,6 @@ func (t *http2Client) socketMetrics() *channelz.EphemeralSocketMetrics { } } -func (t *http2Client) RemoteAddr() net.Addr { return t.remoteAddr } - func (t *http2Client) incrMsgSent() { if channelz.IsOn() { t.channelz.SocketMetrics.MessagesSent.Add(1) diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/http2_server.go index 6f78a6b0c8..a1a14e14fc 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/http2_server.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/http2_server.go @@ -479,13 +479,7 @@ func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeade if t.logger.V(logLevel) { t.logger.Infof("Aborting the stream early: %v", errMsg) } - t.controlBuf.put(&earlyAbortStream{ - httpStatus: http.StatusBadRequest, - streamID: streamID, - contentSubtype: s.contentSubtype, - status: status.New(codes.Internal, errMsg), - rst: !frame.StreamEnded(), - }) + t.writeEarlyAbort(streamID, s.contentSubtype, status.New(codes.Internal, errMsg), http.StatusBadRequest, !frame.StreamEnded()) return nil } @@ -499,23 +493,11 @@ func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeade return nil } if !isGRPC { - t.controlBuf.put(&earlyAbortStream{ - httpStatus: http.StatusUnsupportedMediaType, - streamID: streamID, - contentSubtype: s.contentSubtype, - status: status.Newf(codes.InvalidArgument, "invalid gRPC request content-type %q", contentType), - rst: !frame.StreamEnded(), - }) + t.writeEarlyAbort(streamID, s.contentSubtype, status.Newf(codes.InvalidArgument, "invalid gRPC request content-type %q", contentType), http.StatusUnsupportedMediaType, !frame.StreamEnded()) return nil } if headerError != nil { - t.controlBuf.put(&earlyAbortStream{ - httpStatus: http.StatusBadRequest, - streamID: streamID, - contentSubtype: s.contentSubtype, - status: headerError, - rst: !frame.StreamEnded(), - }) + t.writeEarlyAbort(streamID, s.contentSubtype, headerError, http.StatusBadRequest, !frame.StreamEnded()) return nil } @@ -569,13 +551,7 @@ func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeade if t.logger.V(logLevel) { t.logger.Infof("Aborting the stream early: %v", errMsg) } - t.controlBuf.put(&earlyAbortStream{ - httpStatus: http.StatusMethodNotAllowed, - streamID: streamID, - contentSubtype: s.contentSubtype, - status: status.New(codes.Internal, errMsg), - rst: !frame.StreamEnded(), - }) + t.writeEarlyAbort(streamID, s.contentSubtype, status.New(codes.Internal, errMsg), http.StatusMethodNotAllowed, !frame.StreamEnded()) s.cancel() return nil } @@ -590,27 +566,16 @@ func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeade if !ok { stat = status.New(codes.PermissionDenied, err.Error()) } - t.controlBuf.put(&earlyAbortStream{ - httpStatus: http.StatusOK, - streamID: s.id, - contentSubtype: s.contentSubtype, - status: stat, - rst: !frame.StreamEnded(), - }) + t.writeEarlyAbort(s.id, s.contentSubtype, stat, http.StatusOK, !frame.StreamEnded()) return nil } } if s.ctx.Err() != nil { t.mu.Unlock() + st := status.New(codes.DeadlineExceeded, context.DeadlineExceeded.Error()) // Early abort in case the timeout was zero or so low it already fired. - t.controlBuf.put(&earlyAbortStream{ - httpStatus: http.StatusOK, - streamID: s.id, - contentSubtype: s.contentSubtype, - status: status.New(codes.DeadlineExceeded, context.DeadlineExceeded.Error()), - rst: !frame.StreamEnded(), - }) + t.writeEarlyAbort(s.id, s.contentSubtype, st, http.StatusOK, !frame.StreamEnded()) return nil } @@ -969,13 +934,12 @@ func appendHeaderFieldsFromMD(headerFields []hpack.HeaderField, md metadata.MD) return headerFields } -func (t *http2Server) checkForHeaderListSize(it any) bool { +func (t *http2Server) checkForHeaderListSize(hf []hpack.HeaderField) bool { if t.maxSendHeaderListSize == nil { return true } - hdrFrame := it.(*headerFrame) var sz int64 - for _, f := range hdrFrame.hf { + for _, f := range hf { if sz += int64(f.Size()); sz > int64(*t.maxSendHeaderListSize) { if t.logger.V(logLevel) { t.logger.Infof("Header list size to send violates the maximum size (%d bytes) set by client", *t.maxSendHeaderListSize) @@ -986,6 +950,42 @@ func (t *http2Server) checkForHeaderListSize(it any) bool { return true } +// writeEarlyAbort sends an early abort response with the given HTTP status and +// gRPC status. If the header list size exceeds the peer's limit, it sends a +// RST_STREAM instead. +func (t *http2Server) writeEarlyAbort(streamID uint32, contentSubtype string, stat *status.Status, httpStatus uint32, rst bool) { + hf := []hpack.HeaderField{ + {Name: ":status", Value: strconv.Itoa(int(httpStatus))}, + {Name: "content-type", Value: grpcutil.ContentType(contentSubtype)}, + {Name: "grpc-status", Value: strconv.Itoa(int(stat.Code()))}, + {Name: "grpc-message", Value: encodeGrpcMessage(stat.Message())}, + } + if p := istatus.RawStatusProto(stat); len(p.GetDetails()) > 0 { + stBytes, err := proto.Marshal(p) + if err != nil { + t.logger.Errorf("Failed to marshal rpc status: %s, error: %v", pretty.ToJSON(p), err) + } + if err == nil { + hf = append(hf, hpack.HeaderField{Name: grpcStatusDetailsBinHeader, Value: encodeBinHeader(stBytes)}) + } + } + success, _ := t.controlBuf.executeAndPut(func() bool { + return t.checkForHeaderListSize(hf) + }, &earlyAbortStream{ + streamID: streamID, + rst: rst, + hf: hf, + }) + if !success { + t.controlBuf.put(&cleanupStream{ + streamID: streamID, + rst: true, + rstCode: http2.ErrCodeInternal, + onWrite: func() {}, + }) + } +} + func (t *http2Server) streamContextErr(s *ServerStream) error { select { case <-t.done: @@ -1041,7 +1041,7 @@ func (t *http2Server) writeHeaderLocked(s *ServerStream) error { endStream: false, onWrite: t.setResetPingStrikes, } - success, err := t.controlBuf.executeAndPut(func() bool { return t.checkForHeaderListSize(hf) }, hf) + success, err := t.controlBuf.executeAndPut(func() bool { return t.checkForHeaderListSize(hf.hf) }, hf) if !success { if err != nil { return err @@ -1111,7 +1111,7 @@ func (t *http2Server) writeStatus(s *ServerStream, st *status.Status) error { } success, err := t.controlBuf.executeAndPut(func() bool { - return t.checkForHeaderListSize(trailingHeader) + return t.checkForHeaderListSize(trailingHeader.hf) }, nil) if !success { if err != nil { diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/http_util.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/http_util.go index 6209eb23cd..5bbb641ad9 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/http_util.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/http_util.go @@ -411,12 +411,6 @@ var writeBufferPoolMap = make(map[int]*sync.Pool) var writeBufferMutex sync.Mutex func newFramer(conn io.ReadWriter, writeBufferSize, readBufferSize int, sharedWriteBuffer bool, maxHeaderListSize uint32, memPool mem.BufferPool) *framer { - if memPool == nil { - // Note that this is only supposed to be nil in tests. Otherwise, stream - // is always initialized with a BufferPool. - memPool = mem.DefaultBufferPool() - } - if writeBufferSize < 0 { writeBufferSize = 0 } diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/transport.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/transport.go index 5ff83a7d7d..b86094da94 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/transport.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/internal/transport/transport.go @@ -378,12 +378,28 @@ func (s *Stream) ReadMessageHeader(header []byte) (err error) { return nil } +// ceil returns the ceil after dividing the numerator and denominator while +// avoiding integer overflows. +func ceil(numerator, denominator int) int { + if numerator == 0 { + return 0 + } + return (numerator-1)/denominator + 1 +} + // Read reads n bytes from the wire for this stream. func (s *Stream) read(n int) (data mem.BufferSlice, err error) { // Don't request a read if there was an error earlier if er := s.trReader.er; er != nil { return nil, er } + // gRPC Go accepts data frames with a maximum length of 16KB. Larger + // messages must be split into multiple frames. We pre-allocate the + // buffer to avoid resizing during the read loop, but cap the initial + // capacity to 128 frames (2MB) to prevent over-allocation or panics + // when reading extremely large streams. + allocCap := min(ceil(n, http2MaxFrameLen), 128) + data = make(mem.BufferSlice, 0, allocCap) s.readRequester.requestRead(n) for n != 0 { buf, err := s.trReader.Read(n) @@ -553,6 +569,12 @@ type CallHdr struct { // outbound message. SendCompress string + // AcceptedCompressors overrides the grpc-accept-encoding header for this + // call. When nil, the transport advertises the default set of registered + // compressors. A non-nil pointer overrides that value (including the empty + // string to advertise none). + AcceptedCompressors *string + // Creds specifies credentials.PerRPCCredentials for a call. Creds credentials.PerRPCCredentials @@ -568,9 +590,14 @@ type CallHdr struct { DoneFunc func() // called when the stream is finished - // Authority is used to explicitly override the `:authority` header. If set, - // this value takes precedence over the Host field and will be used as the - // value for the `:authority` header. + // Authority is used to explicitly override the `:authority` header. + // + // This value comes from one of two sources: + // 1. The `CallAuthority` call option, if specified by the user. + // 2. An override provided by the LB picker (e.g. xDS authority rewriting). + // + // The `CallAuthority` call option always takes precedence over the LB + // picker override. Authority string } @@ -590,7 +617,7 @@ type ClientTransport interface { GracefulClose() // NewStream creates a Stream for an RPC. - NewStream(ctx context.Context, callHdr *CallHdr) (*ClientStream, error) + NewStream(ctx context.Context, callHdr *CallHdr, handler stats.Handler) (*ClientStream, error) // Error returns a channel that is closed when some I/O error // happens. Typically the caller should have a goroutine to monitor @@ -608,8 +635,9 @@ type ClientTransport interface { // with a human readable string with debug info. GetGoAwayReason() (GoAwayReason, string) - // RemoteAddr returns the remote network address. - RemoteAddr() net.Addr + // Peer returns information about the peer associated with the Transport. + // The returned information includes authentication and network address details. + Peer() *peer.Peer } // ServerTransport is the common interface for all gRPC server-side transport diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/mem/buffer_pool.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/mem/buffer_pool.go index f211e72745..2ea763a49a 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/mem/buffer_pool.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/mem/buffer_pool.go @@ -38,9 +38,11 @@ type BufferPool interface { Put(*[]byte) } +const goPageSize = 4 << 10 // 4KiB. N.B. this must be a power of 2. + var defaultBufferPoolSizes = []int{ 256, - 4 << 10, // 4KB (go page size) + goPageSize, 16 << 10, // 16KB (max HTTP/2 frame size used by gRPC) 32 << 10, // 32KB (default buffer size for io.Copy) 1 << 20, // 1MB @@ -51,7 +53,7 @@ var defaultBufferPool BufferPool func init() { defaultBufferPool = NewTieredBufferPool(defaultBufferPoolSizes...) - internal.SetDefaultBufferPoolForTesting = func(pool BufferPool) { + internal.SetDefaultBufferPool = func(pool BufferPool) { defaultBufferPool = pool } @@ -172,7 +174,14 @@ func (p *simpleBufferPool) Get(size int) *[]byte { p.pool.Put(bs) } - b := make([]byte, size) + // If we're going to allocate, round up to the nearest page. This way if + // requests frequently arrive with small variation we don't allocate + // repeatedly if we get unlucky and they increase over time. By default we + // only allocate here if size > 1MiB. Because goPageSize is a power of 2, we + // can round up efficiently. + allocSize := (size + goPageSize - 1) & ^(goPageSize - 1) + + b := make([]byte, size, allocSize) return &b } diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/mem/buffers.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/mem/buffers.go index ecbf0b9a73..db1620e6ac 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/mem/buffers.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/mem/buffers.go @@ -62,7 +62,6 @@ var ( bufferPoolingThreshold = 1 << 10 bufferObjectPool = sync.Pool{New: func() any { return new(buffer) }} - refObjectPool = sync.Pool{New: func() any { return new(atomic.Int32) }} ) // IsBelowBufferPoolingThreshold returns true if the given size is less than or @@ -73,9 +72,19 @@ func IsBelowBufferPoolingThreshold(size int) bool { } type buffer struct { + refs atomic.Int32 + data []byte + + // rootBuf is the buffer responsible for returning origData to the pool + // once the reference count drops to 0. + // + // When a buffer is split, the new buffer inherits the rootBuf of the + // original and increments the root's reference count. For the + // initial buffer (the root), this field points to itself. + rootBuf *buffer + + // The following fields are only set for root buffers. origData *[]byte - data []byte - refs *atomic.Int32 pool BufferPool } @@ -103,8 +112,8 @@ func NewBuffer(data *[]byte, pool BufferPool) Buffer { b.origData = data b.data = *data b.pool = pool - b.refs = refObjectPool.Get().(*atomic.Int32) - b.refs.Add(1) + b.rootBuf = b + b.refs.Store(1) return b } @@ -127,42 +136,44 @@ func Copy(data []byte, pool BufferPool) Buffer { } func (b *buffer) ReadOnlyData() []byte { - if b.refs == nil { + if b.rootBuf == nil { panic("Cannot read freed buffer") } return b.data } func (b *buffer) Ref() { - if b.refs == nil { + if b.refs.Add(1) <= 1 { panic("Cannot ref freed buffer") } - b.refs.Add(1) } func (b *buffer) Free() { - if b.refs == nil { + refs := b.refs.Add(-1) + if refs < 0 { panic("Cannot free freed buffer") } - - refs := b.refs.Add(-1) - switch { - case refs > 0: + if refs > 0 { return - case refs == 0: + } + + b.data = nil + if b.rootBuf == b { + // This buffer is the owner of the data slice and its ref count reached + // 0, free the slice. if b.pool != nil { b.pool.Put(b.origData) + b.pool = nil } - - refObjectPool.Put(b.refs) b.origData = nil - b.data = nil - b.refs = nil - b.pool = nil - bufferObjectPool.Put(b) - default: - panic("Cannot free freed buffer") + } else { + // This buffer doesn't own the data slice, decrement a ref on the root + // buffer. + b.rootBuf.Free() } + + b.rootBuf = nil + bufferObjectPool.Put(b) } func (b *buffer) Len() int { @@ -170,16 +181,14 @@ func (b *buffer) Len() int { } func (b *buffer) split(n int) (Buffer, Buffer) { - if b.refs == nil { + if b.rootBuf == nil || b.rootBuf.refs.Add(1) <= 1 { panic("Cannot split freed buffer") } - b.refs.Add(1) split := newBuffer() - split.origData = b.origData split.data = b.data[n:] - split.refs = b.refs - split.pool = b.pool + split.rootBuf = b.rootBuf + split.refs.Store(1) b.data = b.data[:n] @@ -187,7 +196,7 @@ func (b *buffer) split(n int) (Buffer, Buffer) { } func (b *buffer) read(buf []byte) (int, Buffer) { - if b.refs == nil { + if b.rootBuf == nil { panic("Cannot read freed buffer") } diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/resolver/resolver.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/resolver/resolver.go index 8e6af9514b..598ed21a29 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/resolver/resolver.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/resolver/resolver.go @@ -182,6 +182,7 @@ type BuildOptions struct { // An Endpoint is one network endpoint, or server, which may have multiple // addresses with which it can be accessed. +// TODO(i/8773) : make resolver.Endpoint and resolver.Address immutable type Endpoint struct { // Addresses contains a list of addresses used to access this endpoint. Addresses []Address diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/resolver_wrapper.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/resolver_wrapper.go index 80e16a327c..6e61376437 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/resolver_wrapper.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/resolver_wrapper.go @@ -69,6 +69,7 @@ func (ccr *ccResolverWrapper) start() error { errCh := make(chan error) ccr.serializer.TrySchedule(func(ctx context.Context) { if ctx.Err() != nil { + errCh <- ctx.Err() return } opts := resolver.BuildOptions{ diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/rpc_util.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/rpc_util.go index 6b04c9e873..8160f94304 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/rpc_util.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/rpc_util.go @@ -33,6 +33,8 @@ import ( "google.golang.org/grpc/credentials" "google.golang.org/grpc/encoding" "google.golang.org/grpc/encoding/proto" + "google.golang.org/grpc/internal" + "google.golang.org/grpc/internal/grpcutil" "google.golang.org/grpc/internal/transport" "google.golang.org/grpc/mem" "google.golang.org/grpc/metadata" @@ -41,6 +43,10 @@ import ( "google.golang.org/grpc/status" ) +func init() { + internal.AcceptCompressors = acceptCompressors +} + // Compressor defines the interface gRPC uses to compress a message. // // Deprecated: use package encoding. @@ -151,16 +157,32 @@ func (d *gzipDecompressor) Type() string { // callInfo contains all related configuration and information about an RPC. type callInfo struct { - compressorName string - failFast bool - maxReceiveMessageSize *int - maxSendMessageSize *int - creds credentials.PerRPCCredentials - contentSubtype string - codec baseCodec - maxRetryRPCBufferSize int - onFinish []func(err error) - authority string + compressorName string + failFast bool + maxReceiveMessageSize *int + maxSendMessageSize *int + creds credentials.PerRPCCredentials + contentSubtype string + codec baseCodec + maxRetryRPCBufferSize int + onFinish []func(err error) + authority string + acceptedResponseCompressors []string +} + +func acceptedCompressorAllows(allowed []string, name string) bool { + if allowed == nil { + return true + } + if name == "" || name == encoding.Identity { + return true + } + for _, a := range allowed { + if a == name { + return true + } + } + return false } func defaultCallInfo() *callInfo { @@ -170,6 +192,29 @@ func defaultCallInfo() *callInfo { } } +func newAcceptedCompressionConfig(names []string) ([]string, error) { + if len(names) == 0 { + return nil, nil + } + var allowed []string + seen := make(map[string]struct{}, len(names)) + for _, name := range names { + name = strings.TrimSpace(name) + if name == "" || name == encoding.Identity { + continue + } + if !grpcutil.IsCompressorNameRegistered(name) { + return nil, status.Errorf(codes.InvalidArgument, "grpc: compressor %q is not registered", name) + } + if _, dup := seen[name]; dup { + continue + } + seen[name] = struct{}{} + allowed = append(allowed, name) + } + return allowed, nil +} + // CallOption configures a Call before it starts or extracts information from // a Call after it completes. type CallOption interface { @@ -471,6 +516,31 @@ func (o CompressorCallOption) before(c *callInfo) error { } func (o CompressorCallOption) after(*callInfo, *csAttempt) {} +// acceptCompressors returns a CallOption that limits the compression algorithms +// advertised in the grpc-accept-encoding header for response messages. +// Compression algorithms not in the provided list will not be advertised, and +// responses compressed with non-listed algorithms will be rejected. +func acceptCompressors(names ...string) CallOption { + cp := append([]string(nil), names...) + return acceptCompressorsCallOption{names: cp} +} + +// acceptCompressorsCallOption is a CallOption that limits response compression. +type acceptCompressorsCallOption struct { + names []string +} + +func (o acceptCompressorsCallOption) before(c *callInfo) error { + allowed, err := newAcceptedCompressionConfig(o.names) + if err != nil { + return err + } + c.acceptedResponseCompressors = allowed + return nil +} + +func (acceptCompressorsCallOption) after(*callInfo, *csAttempt) {} + // CallContentSubtype returns a CallOption that will set the content-subtype // for a call. For example, if content-subtype is "json", the Content-Type over // the wire will be "application/grpc+json". The content-subtype is converted @@ -857,8 +927,7 @@ func (p *payloadInfo) free() { // the buffer is no longer needed. // TODO: Refactor this function to reduce the number of arguments. // See: https://google.github.io/styleguide/go/best-practices.html#function-argument-lists -func recvAndDecompress(p *parser, s recvCompressor, dc Decompressor, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor, isServer bool, -) (out mem.BufferSlice, err error) { +func recvAndDecompress(p *parser, s recvCompressor, dc Decompressor, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor, isServer bool) (out mem.BufferSlice, err error) { pf, compressed, err := p.recvMsg(maxReceiveMessageSize) if err != nil { return nil, err diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/server.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/server.go index ddd3773411..8efb29a7b9 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/server.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/server.go @@ -42,6 +42,7 @@ import ( "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/binarylog" "google.golang.org/grpc/internal/channelz" + "google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/grpcutil" istats "google.golang.org/grpc/internal/stats" @@ -149,6 +150,8 @@ type Server struct { serverWorkerChannel chan func() serverWorkerChannelClose func() + + strictPathCheckingLogEmitted atomic.Bool } type serverOptions struct { @@ -923,9 +926,7 @@ func (s *Server) Serve(lis net.Listener) error { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 - } - if max := 1 * time.Second; tempDelay > max { - tempDelay = max + tempDelay = min(tempDelay, 1*time.Second) } s.mu.Lock() s.printf("Accept error: %v; retrying in %v", err, tempDelay) @@ -1764,6 +1765,24 @@ func (s *Server) processStreamingRPC(ctx context.Context, stream *transport.Serv return ss.s.WriteStatus(statusOK) } +func (s *Server) handleMalformedMethodName(stream *transport.ServerStream, ti *traceInfo) { + if ti != nil { + ti.tr.LazyLog(&fmtStringer{"Malformed method name %q", []any{stream.Method()}}, true) + ti.tr.SetError() + } + errDesc := fmt.Sprintf("malformed method name: %q", stream.Method()) + if err := stream.WriteStatus(status.New(codes.Unimplemented, errDesc)); err != nil { + if ti != nil { + ti.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true) + ti.tr.SetError() + } + channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream failed to write status: %v", err) + } + if ti != nil { + ti.tr.Finish() + } +} + func (s *Server) handleStream(t transport.ServerTransport, stream *transport.ServerStream) { ctx := stream.Context() ctx = contextWithServer(ctx, s) @@ -1784,26 +1803,30 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Ser } sm := stream.Method() - if sm != "" && sm[0] == '/' { + if sm == "" { + s.handleMalformedMethodName(stream, ti) + return + } + if sm[0] != '/' { + // TODO(easwars): Add a link to the CVE in the below log messages once + // published. + if envconfig.DisableStrictPathChecking { + if old := s.strictPathCheckingLogEmitted.Swap(true); !old { + channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream received malformed method name %q. Allowing it because the environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING is set to true, but this option will be removed in a future release.", sm) + } + } else { + if old := s.strictPathCheckingLogEmitted.Swap(true); !old { + channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream rejected malformed method name %q. To temporarily allow such requests, set the environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to true. Note that this is not recommended as it may allow requests to bypass security policies.", sm) + } + s.handleMalformedMethodName(stream, ti) + return + } + } else { sm = sm[1:] } pos := strings.LastIndex(sm, "/") if pos == -1 { - if ti != nil { - ti.tr.LazyLog(&fmtStringer{"Malformed method name %q", []any{sm}}, true) - ti.tr.SetError() - } - errDesc := fmt.Sprintf("malformed method name: %q", stream.Method()) - if err := stream.WriteStatus(status.New(codes.Unimplemented, errDesc)); err != nil { - if ti != nil { - ti.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true) - ti.tr.SetError() - } - channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream failed to write status: %v", err) - } - if ti != nil { - ti.tr.Finish() - } + s.handleMalformedMethodName(stream, ti) return } service := sm[:pos] diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/stream.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/stream.go index ca87ff9776..eedb5f9b99 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/stream.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/stream.go @@ -25,6 +25,7 @@ import ( "math" rand "math/rand/v2" "strconv" + "strings" "sync" "time" @@ -51,7 +52,8 @@ import ( var metadataFromOutgoingContextRaw = internal.FromOutgoingContextRaw.(func(context.Context) (metadata.MD, [][]string, bool)) // StreamHandler defines the handler called by gRPC server to complete the -// execution of a streaming RPC. +// execution of a streaming RPC. srv is the service implementation on which the +// RPC was invoked. // // If a StreamHandler returns an error, it should either be produced by the // status package, or be one of the context errors. Otherwise, gRPC will use @@ -179,13 +181,41 @@ func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth var emptyMethodConfig = serviceconfig.MethodConfig{} +// endOfClientStream performs cleanup actions required for both successful and +// failed streams. This includes incrementing channelz stats and invoking all +// registered OnFinish call options. +func endOfClientStream(cc *ClientConn, err error, opts ...CallOption) { + if channelz.IsOn() { + if err != nil { + cc.incrCallsFailed() + } else { + cc.incrCallsSucceeded() + } + } + + for _, o := range opts { + if o, ok := o.(OnFinishCallOption); ok { + o.OnFinish(err) + } + } +} + func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) { + if channelz.IsOn() { + cc.incrCallsStarted() + } + defer func() { + if err != nil { + // Ensure cleanup when stream creation fails. + endOfClientStream(cc, err, opts...) + } + }() + // Start tracking the RPC for idleness purposes. This is where a stream is // created for both streaming and unary RPCs, and hence is a good place to // track active RPC count. - if err := cc.idlenessMgr.OnCallBegin(); err != nil { - return nil, err - } + cc.idlenessMgr.OnCallBegin() + // Add a calloption, to decrement the active call count, that gets executed // when the RPC completes. opts = append([]CallOption{OnFinish(func(error) { cc.idlenessMgr.OnCallEnd() })}, opts...) @@ -204,14 +234,6 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth } } } - if channelz.IsOn() { - cc.incrCallsStarted() - defer func() { - if err != nil { - cc.incrCallsFailed() - } - }() - } // Provide an opportunity for the first RPC to see the first service config // provided by the resolver. nameResolutionDelayed, err := cc.waitForResolvedAddrs(ctx) @@ -301,6 +323,10 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client DoneFunc: doneFunc, Authority: callInfo.authority, } + if allowed := callInfo.acceptedResponseCompressors; len(allowed) > 0 { + headerValue := strings.Join(allowed, ",") + callHdr.AcceptedCompressors = &headerValue + } // Set our outgoing compression according to the UseCompressor CallOption, if // set. In that case, also find the compressor from the encoding package. @@ -484,7 +510,7 @@ func (a *csAttempt) getTransport() error { return err } if a.trInfo != nil { - a.trInfo.firstLine.SetRemoteAddr(a.transport.RemoteAddr()) + a.trInfo.firstLine.SetRemoteAddr(a.transport.Peer().Addr) } if pick.blocked && a.statsHandler != nil { a.statsHandler.HandleRPC(a.ctx, &stats.DelayedPickComplete{}) @@ -512,9 +538,17 @@ func (a *csAttempt) newStream() error { md, _ := metadata.FromOutgoingContext(a.ctx) md = metadata.Join(md, a.pickResult.Metadata) a.ctx = metadata.NewOutgoingContext(a.ctx, md) - } - s, err := a.transport.NewStream(a.ctx, cs.callHdr) + // If the `CallAuthority` CallOption is not set, check if the LB picker + // has provided an authority override in the PickResult metadata and + // apply it, as specified in gRFC A81. + if cs.callInfo.authority == "" { + if authMD := a.pickResult.Metadata.Get(":authority"); len(authMD) > 0 { + cs.callHdr.Authority = authMD[0] + } + } + } + s, err := a.transport.NewStream(a.ctx, cs.callHdr, a.statsHandler) if err != nil { nse, ok := err.(*transport.NewStreamError) if !ok { @@ -1042,9 +1076,6 @@ func (cs *clientStream) finish(err error) { return } cs.finished = true - for _, onFinish := range cs.callInfo.onFinish { - onFinish(err) - } cs.commitAttemptLocked() if cs.attempt != nil { cs.attempt.finish(err) @@ -1084,13 +1115,7 @@ func (cs *clientStream) finish(err error) { if err == nil { cs.retryThrottler.successfulRPC() } - if channelz.IsOn() { - if err != nil { - cs.cc.incrCallsFailed() - } else { - cs.cc.incrCallsSucceeded() - } - } + endOfClientStream(cs.cc, err, cs.opts...) cs.cancel() } @@ -1134,6 +1159,10 @@ func (a *csAttempt) recvMsg(m any, payInfo *payloadInfo) (err error) { a.decompressorV0 = nil a.decompressorV1 = encoding.GetCompressor(ct) } + // Validate that the compression method is acceptable for this call. + if !acceptedCompressorAllows(cs.callInfo.acceptedResponseCompressors, ct) { + return status.Errorf(codes.Internal, "grpc: peer compressed the response with %q which is not allowed by AcceptCompressors", ct) + } } else { // No compression is used; disable our decompressor. a.decompressorV0 = nil @@ -1321,10 +1350,12 @@ func newNonRetryClientStream(ctx context.Context, desc *StreamDesc, method strin codec: c.codec, sendCompressorV0: cp, sendCompressorV1: comp, + decompressorV0: ac.cc.dopts.dc, transport: t, } - s, err := as.transport.NewStream(as.ctx, as.callHdr) + // nil stats handler: internal streams like health and ORCA do not support telemetry. + s, err := as.transport.NewStream(as.ctx, as.callHdr, nil) if err != nil { err = toRPCErr(err) return nil, err @@ -1479,6 +1510,10 @@ func (as *addrConnStream) RecvMsg(m any) (err error) { as.decompressorV0 = nil as.decompressorV1 = encoding.GetCompressor(ct) } + // Validate that the compression method is acceptable for this call. + if !acceptedCompressorAllows(as.callInfo.acceptedResponseCompressors, ct) { + return status.Errorf(codes.Internal, "grpc: peer compressed the response with %q which is not allowed by AcceptCompressors", ct) + } } else { // No compression is used; disable our decompressor. as.decompressorV0 = nil diff --git a/cluster-api/cluster-api/vendor/google.golang.org/grpc/version.go b/cluster-api/cluster-api/vendor/google.golang.org/grpc/version.go index 9e6d018fb7..76c2eed773 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/grpc/version.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/grpc/version.go @@ -19,4 +19,4 @@ package grpc // Version is the current grpc version. -const Version = "1.77.0" +const Version = "1.79.3" diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go index 669133d04d..c96e448346 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go @@ -32,7 +32,7 @@ var byteType = reflect.TypeOf(byte(0)) func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescriptors) protoreflect.FieldDescriptor { f := new(filedesc.Field) f.L0.ParentFile = filedesc.SurrogateProto2 - f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures + packed := false for len(tag) > 0 { i := strings.IndexByte(tag, ',') if i < 0 { @@ -108,7 +108,7 @@ func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescri f.L1.StringName.InitJSON(jsonName) } case s == "packed": - f.L1.EditionFeatures.IsPacked = true + packed = true case strings.HasPrefix(s, "def="): // The default tag is special in that everything afterwards is the // default regardless of the presence of commas. @@ -121,6 +121,13 @@ func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescri tag = strings.TrimPrefix(tag[i:], ",") } + // Update EditionFeatures after the loop and after we know whether this is + // a proto2 or proto3 field. + f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures + if packed { + f.L1.EditionFeatures.IsPacked = true + } + // The generator uses the group message name instead of the field name. // We obtain the real field name by lowercasing the group name. if f.L1.Kind == protoreflect.GroupKind { diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go index 099b2bf451..9aa7a9bb77 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go @@ -424,27 +424,34 @@ func (d *Decoder) parseFieldName() (tok Token, err error) { return Token{}, d.newSyntaxError("invalid field name: %s", errId(d.in)) } -// parseTypeName parses Any type URL or extension field name. The name is -// enclosed in [ and ] characters. The C++ parser does not handle many legal URL -// strings. This implementation is more liberal and allows for the pattern -// ^[-_a-zA-Z0-9]+([./][-_a-zA-Z0-9]+)*`). Whitespaces and comments are allowed -// in between [ ], '.', '/' and the sub names. +// parseTypeName parses an Any type URL or an extension field name. The name is +// enclosed in [ and ] characters. We allow almost arbitrary type URL prefixes, +// closely following the text-format spec [1,2]. We implement "ExtensionName | +// AnyName" as follows (with some exceptions for backwards compatibility): +// +// char = [-_a-zA-Z0-9] +// url_char = char | [.~!$&'()*+,;=] | "%", hex, hex +// +// Ident = char, { char } +// TypeName = Ident, { ".", Ident } ; +// UrlPrefix = url_char, { url_char | "/" } ; +// ExtensionName = "[", TypeName, "]" ; +// AnyName = "[", UrlPrefix, "/", TypeName, "]" ; +// +// Additionally, we allow arbitrary whitespace and comments between [ and ]. +// +// [1] https://protobuf.dev/reference/protobuf/textformat-spec/#characters +// [2] https://protobuf.dev/reference/protobuf/textformat-spec/#field-names func (d *Decoder) parseTypeName() (Token, error) { - startPos := len(d.orig) - len(d.in) // Use alias s to advance first in order to use d.in for error handling. - // Caller already checks for [ as first character. + // Caller already checks for [ as first character (d.in[0] == '['). s := consume(d.in[1:], 0) if len(s) == 0 { return Token{}, ErrUnexpectedEOF } + // Collect everything between [ and ] in name. var name []byte - for len(s) > 0 && isTypeNameChar(s[0]) { - name = append(name, s[0]) - s = s[1:] - } - s = consume(s, 0) - var closed bool for len(s) > 0 && !closed { switch { @@ -452,23 +459,20 @@ func (d *Decoder) parseTypeName() (Token, error) { s = s[1:] closed = true - case s[0] == '/', s[0] == '.': - if len(name) > 0 && (name[len(name)-1] == '/' || name[len(name)-1] == '.') { - return Token{}, d.newSyntaxError("invalid type URL/extension field name: %s", - d.orig[startPos:len(d.orig)-len(s)+1]) - } + case s[0] == '/' || isTypeNameChar(s[0]) || isUrlExtraChar(s[0]): name = append(name, s[0]) - s = s[1:] - s = consume(s, 0) - for len(s) > 0 && isTypeNameChar(s[0]) { - name = append(name, s[0]) - s = s[1:] + s = consume(s[1:], 0) + + // URL percent-encoded chars + case s[0] == '%': + if len(s) < 3 || !isHexChar(s[1]) || !isHexChar(s[2]) { + return Token{}, d.parseTypeNameError(s, 3) } - s = consume(s, 0) + name = append(name, s[0], s[1], s[2]) + s = consume(s[3:], 0) default: - return Token{}, d.newSyntaxError( - "invalid type URL/extension field name: %s", d.orig[startPos:len(d.orig)-len(s)+1]) + return Token{}, d.parseTypeNameError(s, 1) } } @@ -476,15 +480,38 @@ func (d *Decoder) parseTypeName() (Token, error) { return Token{}, ErrUnexpectedEOF } - // First character cannot be '.'. Last character cannot be '.' or '/'. - size := len(name) - if size == 0 || name[0] == '.' || name[size-1] == '.' || name[size-1] == '/' { - return Token{}, d.newSyntaxError("invalid type URL/extension field name: %s", - d.orig[startPos:len(d.orig)-len(s)]) + // Split collected name on last '/' into urlPrefix and typeName (if '/' is + // present). + typeName := name + if i := bytes.LastIndexByte(name, '/'); i != -1 { + urlPrefix := name[:i] + typeName = name[i+1:] + + // urlPrefix may be empty (for backwards compatibility). + // If non-empty, it must not start with '/'. + if len(urlPrefix) > 0 && urlPrefix[0] == '/' { + return Token{}, d.parseTypeNameError(s, 0) + } } + // typeName must not be empty (note: "" splits to [""]) and all identifier + // parts must not be empty. + for _, ident := range bytes.Split(typeName, []byte{'.'}) { + if len(ident) == 0 { + return Token{}, d.parseTypeNameError(s, 0) + } + } + + // typeName must not contain any percent-encoded or special URL chars. + for _, b := range typeName { + if b == '%' || (b != '.' && isUrlExtraChar(b)) { + return Token{}, d.parseTypeNameError(s, 0) + } + } + + startPos := len(d.orig) - len(d.in) + endPos := len(d.orig) - len(s) d.in = s - endPos := len(d.orig) - len(d.in) d.consume(0) return Token{ @@ -496,16 +523,32 @@ func (d *Decoder) parseTypeName() (Token, error) { }, nil } +func (d *Decoder) parseTypeNameError(s []byte, numUnconsumedChars int) error { + return d.newSyntaxError( + "invalid type URL/extension field name: %s", + d.in[:len(d.in)-len(s)+min(numUnconsumedChars, len(s))], + ) +} + +func isHexChar(b byte) bool { + return ('0' <= b && b <= '9') || + ('a' <= b && b <= 'f') || + ('A' <= b && b <= 'F') +} + func isTypeNameChar(b byte) bool { - return (b == '-' || b == '_' || + return b == '-' || b == '_' || ('0' <= b && b <= '9') || ('a' <= b && b <= 'z') || - ('A' <= b && b <= 'Z')) + ('A' <= b && b <= 'Z') } -func isWhiteSpace(b byte) bool { +// isUrlExtraChar complements isTypeNameChar with extra characters that we allow +// in URLs but not in type names. Note that '/' is not included so that it can +// be treated specially. +func isUrlExtraChar(b byte) bool { switch b { - case ' ', '\n', '\r', '\t': + case '.', '~', '!', '$', '&', '(', ')', '*', '+', ',', ';', '=': return true default: return false diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/filedesc/desc.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/filedesc/desc.go index dbcf90b871..c775e5832f 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/filedesc/desc.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/filedesc/desc.go @@ -32,6 +32,7 @@ const ( EditionProto3 Edition = 999 Edition2023 Edition = 1000 Edition2024 Edition = 1001 + EditionUnstable Edition = 9999 EditionUnsupported Edition = 100000 ) diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go index dd31faaeb0..78f02b1b49 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go @@ -330,7 +330,6 @@ func (md *Message) unmarshalFull(b []byte, sb *strs.Builder) { md.L1.Extensions.List[extensionIdx].unmarshalFull(v, sb) extensionIdx++ case genid.DescriptorProto_Options_field_number: - md.unmarshalOptions(v) rawOptions = appendOptions(rawOptions, v) } default: @@ -356,27 +355,6 @@ func (md *Message) unmarshalFull(b []byte, sb *strs.Builder) { md.L2.Options = md.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Message, rawOptions) } -func (md *Message) unmarshalOptions(b []byte) { - for len(b) > 0 { - num, typ, n := protowire.ConsumeTag(b) - b = b[n:] - switch typ { - case protowire.VarintType: - v, m := protowire.ConsumeVarint(b) - b = b[m:] - switch num { - case genid.MessageOptions_MapEntry_field_number: - md.L1.IsMapEntry = protowire.DecodeBool(v) - case genid.MessageOptions_MessageSetWireFormat_field_number: - md.L1.IsMessageSet = protowire.DecodeBool(v) - } - default: - m := protowire.ConsumeFieldValue(num, typ, b) - b = b[m:] - } - } -} - func unmarshalMessageReservedRange(b []byte) (r [2]protoreflect.FieldNumber) { for len(b) > 0 { num, typ, n := protowire.ConsumeTag(b) diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go index 950a6a325a..65aaf4d210 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go @@ -26,6 +26,7 @@ const ( Edition_EDITION_PROTO3_enum_value = 999 Edition_EDITION_2023_enum_value = 1000 Edition_EDITION_2024_enum_value = 1001 + Edition_EDITION_UNSTABLE_enum_value = 9999 Edition_EDITION_1_TEST_ONLY_enum_value = 1 Edition_EDITION_2_TEST_ONLY_enum_value = 2 Edition_EDITION_99997_TEST_ONLY_enum_value = 99997 diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/impl/codec_map.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/impl/codec_map.go index 229c698013..4a3bf393ef 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/impl/codec_map.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/impl/codec_map.go @@ -113,6 +113,9 @@ func sizeMap(mapv reflect.Value, mapi *mapInfo, f *coderFieldInfo, opts marshalO } func consumeMap(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { + if opts.depth--; opts.depth < 0 { + return out, errRecursionDepth + } if wtyp != protowire.BytesType { return out, errUnknown } @@ -170,6 +173,9 @@ func consumeMap(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo } func consumeMapOfMessage(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { + if opts.depth--; opts.depth < 0 { + return out, errRecursionDepth + } if wtyp != protowire.BytesType { return out, errUnknown } diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/impl/decode.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/impl/decode.go index e0dd21fa5f..1228b5c8c2 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/impl/decode.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/impl/decode.go @@ -102,8 +102,7 @@ var errUnknown = errors.New("unknown") func (mi *MessageInfo) unmarshalPointer(b []byte, p pointer, groupTag protowire.Number, opts unmarshalOptions) (out unmarshalOutput, err error) { mi.init() - opts.depth-- - if opts.depth < 0 { + if opts.depth--; opts.depth < 0 { return out, errRecursionDepth } if flags.ProtoLegacy && mi.isMessageSet { diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/impl/validate.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/impl/validate.go index 7b2995dde5..99a1eb95f7 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/impl/validate.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/impl/validate.go @@ -68,9 +68,13 @@ func Validate(mt protoreflect.MessageType, in protoiface.UnmarshalInput) (out pr if in.Resolver == nil { in.Resolver = protoregistry.GlobalTypes } + if in.Depth == 0 { + in.Depth = protowire.DefaultRecursionLimit + } o, st := mi.validate(in.Buf, 0, unmarshalOptions{ flags: in.Flags, resolver: in.Resolver, + depth: in.Depth, }) if o.initialized { out.Flags |= protoiface.UnmarshalInitialized @@ -257,6 +261,9 @@ func (mi *MessageInfo) validate(b []byte, groupTag protowire.Number, opts unmars states[0].typ = validationTypeGroup states[0].endGroup = groupTag } + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } initialized := true start := len(b) State: @@ -451,6 +458,13 @@ State: mi: vi.mi, tail: b, }) + if vi.typ == validationTypeMessage || + vi.typ == validationTypeGroup || + vi.typ == validationTypeMap { + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } + } b = v continue State case validationTypeRepeatedVarint: @@ -499,6 +513,9 @@ State: mi: vi.mi, endGroup: num, }) + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } continue State case flags.ProtoLegacy && vi.typ == validationTypeMessageSetItem: typeid, v, n, err := messageset.ConsumeFieldValue(b, false) @@ -521,6 +538,13 @@ State: mi: xvi.mi, tail: b[n:], }) + if xvi.typ == validationTypeMessage || + xvi.typ == validationTypeGroup || + xvi.typ == validationTypeMap { + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } + } b = v continue State } @@ -547,12 +571,14 @@ State: switch st.typ { case validationTypeMessage, validationTypeGroup: numRequiredFields = int(st.mi.numRequiredFields) + opts.depth++ case validationTypeMap: // If this is a map field with a message value that contains // required fields, require that the value be present. if st.mi != nil && st.mi.numRequiredFields > 0 { numRequiredFields = 1 } + opts.depth++ } // If there are more than 64 required fields, this check will // always fail and we will report that the message is potentially diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/version/version.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/version/version.go index 77de0f238c..763fd82841 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/version/version.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/internal/version/version.go @@ -52,7 +52,7 @@ import ( const ( Major = 1 Minor = 36 - Patch = 10 + Patch = 11 PreRelease = "" ) diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/proto/decode.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/proto/decode.go index 4cbf1aeaf7..889d8511d2 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/proto/decode.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/proto/decode.go @@ -121,9 +121,8 @@ func (o UnmarshalOptions) unmarshal(b []byte, m protoreflect.Message) (out proto out, err = methods.Unmarshal(in) } else { - o.RecursionLimit-- - if o.RecursionLimit < 0 { - return out, errors.New("exceeded max recursion depth") + if o.RecursionLimit--; o.RecursionLimit < 0 { + return out, errRecursionDepth } err = o.unmarshalMessageSlow(b, m) } @@ -220,6 +219,9 @@ func (o UnmarshalOptions) unmarshalSingular(b []byte, wtyp protowire.Type, m pro } func (o UnmarshalOptions) unmarshalMap(b []byte, wtyp protowire.Type, mapv protoreflect.Map, fd protoreflect.FieldDescriptor) (n int, err error) { + if o.RecursionLimit--; o.RecursionLimit < 0 { + return 0, errRecursionDepth + } if wtyp != protowire.BytesType { return 0, errUnknown } @@ -305,3 +307,5 @@ func (o UnmarshalOptions) unmarshalMap(b []byte, wtyp protowire.Type, mapv proto var errUnknown = errors.New("BUG: internal error (unknown)") var errDecode = errors.New("cannot parse invalid wire-format data") + +var errRecursionDepth = errors.New("exceeded maximum recursion depth") diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go index 9196288e4a..40f17af4e3 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go @@ -108,7 +108,9 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot if f.L1.Path == "" { return nil, errors.New("file path must be populated") } - if f.L1.Syntax == protoreflect.Editions && (fd.GetEdition() < editionssupport.Minimum || fd.GetEdition() > editionssupport.Maximum) { + if f.L1.Syntax == protoreflect.Editions && + (fd.GetEdition() < editionssupport.Minimum || fd.GetEdition() > editionssupport.Maximum) && + fd.GetEdition() != descriptorpb.Edition_EDITION_UNSTABLE { // Allow cmd/protoc-gen-go/testdata to use any edition for easier // testing of upcoming edition features. if !strings.HasPrefix(fd.GetName(), "cmd/protoc-gen-go/testdata/") { @@ -152,6 +154,7 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot imp := &f.L2.Imports[i] imps.importPublic(imp.Imports()) } + optionImps := importSet{f.Path(): true} if len(fd.GetOptionDependency()) > 0 { optionImports := make(filedesc.FileImports, len(fd.GetOptionDependency())) for i, path := range fd.GetOptionDependency() { @@ -165,10 +168,12 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot } imp.FileDescriptor = f - if imps[imp.Path()] { + if imps[imp.Path()] || optionImps[imp.Path()] { return nil, errors.New("already imported %q", path) } - imps[imp.Path()] = true + // This needs to be a separate map so that we don't recognize non-options + // symbols coming from option imports. + optionImps[imp.Path()] = true } f.L2.OptionImports = func() protoreflect.FileImports { return &optionImports diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go index 697a61b290..147b8c7398 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go @@ -46,6 +46,8 @@ func toEditionProto(ed filedesc.Edition) descriptorpb.Edition { return descriptorpb.Edition_EDITION_2023 case filedesc.Edition2024: return descriptorpb.Edition_EDITION_2024 + case filedesc.EditionUnstable: + return descriptorpb.Edition_EDITION_UNSTABLE default: panic(fmt.Sprintf("unknown value for edition: %v", ed)) } @@ -58,7 +60,7 @@ func getFeatureSetFor(ed filedesc.Edition) *descriptorpb.FeatureSet { return def } edpb := toEditionProto(ed) - if defaults.GetMinimumEdition() > edpb || defaults.GetMaximumEdition() < edpb { + if (defaults.GetMinimumEdition() > edpb || defaults.GetMaximumEdition() < edpb) && edpb != descriptorpb.Edition_EDITION_UNSTABLE { // This should never happen protodesc.(FileOptions).New would fail when // initializing the file descriptor. // This most likely means the embedded defaults were not updated. diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go index 4eacb523c3..0b23faa957 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go @@ -69,6 +69,8 @@ const ( // comparison. Edition_EDITION_2023 Edition = 1000 Edition_EDITION_2024 Edition = 1001 + // A placeholder edition for developing and testing unscheduled features. + Edition_EDITION_UNSTABLE Edition = 9999 // Placeholder editions for testing feature resolution. These should not be // used or relied on outside of tests. Edition_EDITION_1_TEST_ONLY Edition = 1 @@ -91,6 +93,7 @@ var ( 999: "EDITION_PROTO3", 1000: "EDITION_2023", 1001: "EDITION_2024", + 9999: "EDITION_UNSTABLE", 1: "EDITION_1_TEST_ONLY", 2: "EDITION_2_TEST_ONLY", 99997: "EDITION_99997_TEST_ONLY", @@ -105,6 +108,7 @@ var ( "EDITION_PROTO3": 999, "EDITION_2023": 1000, "EDITION_2024": 1001, + "EDITION_UNSTABLE": 9999, "EDITION_1_TEST_ONLY": 1, "EDITION_2_TEST_ONLY": 2, "EDITION_99997_TEST_ONLY": 99997, @@ -4793,11 +4797,11 @@ const file_google_protobuf_descriptor_proto_rawDesc = "" + "\x18EnumValueDescriptorProto\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n" + "\x06number\x18\x02 \x01(\x05R\x06number\x12;\n" + - "\aoptions\x18\x03 \x01(\v2!.google.protobuf.EnumValueOptionsR\aoptions\"\xa7\x01\n" + + "\aoptions\x18\x03 \x01(\v2!.google.protobuf.EnumValueOptionsR\aoptions\"\xb5\x01\n" + "\x16ServiceDescriptorProto\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12>\n" + "\x06method\x18\x02 \x03(\v2&.google.protobuf.MethodDescriptorProtoR\x06method\x129\n" + - "\aoptions\x18\x03 \x01(\v2\x1f.google.protobuf.ServiceOptionsR\aoptions\"\x89\x02\n" + + "\aoptions\x18\x03 \x01(\v2\x1f.google.protobuf.ServiceOptionsR\aoptionsJ\x04\b\x04\x10\x05R\x06stream\"\x89\x02\n" + "\x15MethodDescriptorProto\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n" + "\n" + @@ -5033,14 +5037,15 @@ const file_google_protobuf_descriptor_proto_rawDesc = "" + "\bSemantic\x12\b\n" + "\x04NONE\x10\x00\x12\a\n" + "\x03SET\x10\x01\x12\t\n" + - "\x05ALIAS\x10\x02*\xa7\x02\n" + + "\x05ALIAS\x10\x02*\xbe\x02\n" + "\aEdition\x12\x13\n" + "\x0fEDITION_UNKNOWN\x10\x00\x12\x13\n" + "\x0eEDITION_LEGACY\x10\x84\a\x12\x13\n" + "\x0eEDITION_PROTO2\x10\xe6\a\x12\x13\n" + "\x0eEDITION_PROTO3\x10\xe7\a\x12\x11\n" + "\fEDITION_2023\x10\xe8\a\x12\x11\n" + - "\fEDITION_2024\x10\xe9\a\x12\x17\n" + + "\fEDITION_2024\x10\xe9\a\x12\x15\n" + + "\x10EDITION_UNSTABLE\x10\x8fN\x12\x17\n" + "\x13EDITION_1_TEST_ONLY\x10\x01\x12\x17\n" + "\x13EDITION_2_TEST_ONLY\x10\x02\x12\x1d\n" + "\x17EDITION_99997_TEST_ONLY\x10\x9d\x8d\x06\x12\x1d\n" + diff --git a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go index 06d584c14b..484c21fd53 100644 --- a/cluster-api/cluster-api/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go +++ b/cluster-api/cluster-api/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go @@ -172,13 +172,14 @@ import ( // ) to obtain a formatter capable of generating timestamps in this format. type Timestamp struct { state protoimpl.MessageState `protogen:"open.v1"` - // Represents seconds of UTC time since Unix epoch - // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to - // 9999-12-31T23:59:59Z inclusive. + // Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must + // be between -315576000000 and 315576000000 inclusive (which corresponds to + // 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z). Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` - // Non-negative fractions of a second at nanosecond resolution. Negative - // second values with fractions must still have non-negative nanos values - // that count forward in time. Must be from 0 to 999,999,999 + // Non-negative fractions of a second at nanosecond resolution. This field is + // the nanosecond portion of the duration, not an alternative to seconds. + // Negative second values with fractions must still have non-negative nanos + // values that count forward in time. Must be between 0 and 999,999,999 // inclusive. Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` unknownFields protoimpl.UnknownFields diff --git a/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/controller.go b/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/controller.go index 5f983b6b63..e07c04e625 100644 --- a/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/controller.go +++ b/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/controller.go @@ -596,16 +596,7 @@ func newInformer(clientState Store, options InformerOptions) Controller { // KeyLister, that way resync operations will result in the correct set // of update/delete deltas. - var fifo Queue - if clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.InOrderInformers) { - fifo = NewRealFIFO(MetaNamespaceKeyFunc, clientState, options.Transform) - } else { - fifo = NewDeltaFIFOWithOptions(DeltaFIFOOptions{ - KnownObjects: clientState, - EmitDeltaTypeReplaced: true, - Transformer: options.Transform, - }) - } + fifo := newQueueFIFO(clientState, options.Transform) cfg := &Config{ Queue: fifo, @@ -623,3 +614,15 @@ func newInformer(clientState Store, options InformerOptions) Controller { } return New(cfg) } + +func newQueueFIFO(clientState Store, transform TransformFunc) Queue { + if clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.InOrderInformers) { + return NewRealFIFO(MetaNamespaceKeyFunc, clientState, transform) + } else { + return NewDeltaFIFOWithOptions(DeltaFIFOOptions{ + KnownObjects: clientState, + EmitDeltaTypeReplaced: true, + Transformer: transform, + }) + } +} diff --git a/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/delta_fifo.go b/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/delta_fifo.go index 9d9e238ccc..a0d7a834aa 100644 --- a/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/delta_fifo.go +++ b/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/delta_fifo.go @@ -270,7 +270,8 @@ func NewDeltaFIFOWithOptions(opts DeltaFIFOOptions) *DeltaFIFO { } var ( - _ = Queue(&DeltaFIFO{}) // DeltaFIFO is a Queue + _ = Queue(&DeltaFIFO{}) // DeltaFIFO is a Queue + _ = TransformingStore(&DeltaFIFO{}) // DeltaFIFO implements TransformingStore to allow memory optimizations ) var ( diff --git a/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/reflector.go b/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/reflector.go index ee9be77278..6fd43375f6 100644 --- a/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/reflector.go +++ b/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/reflector.go @@ -80,7 +80,7 @@ type ReflectorStore interface { // TransformingStore is an optional interface that can be implemented by the provided store. // If implemented on the provided store reflector will use the same transformer in its internal stores. type TransformingStore interface { - Store + ReflectorStore Transformer() TransformFunc } @@ -726,9 +726,11 @@ func (r *Reflector) watchList(ctx context.Context) (watch.Interface, error) { return false } + var transformer TransformFunc storeOpts := []StoreOption{} if tr, ok := r.store.(TransformingStore); ok && tr.Transformer() != nil { - storeOpts = append(storeOpts, WithTransformer(tr.Transformer())) + transformer = tr.Transformer() + storeOpts = append(storeOpts, WithTransformer(transformer)) } initTrace := trace.New("Reflector WatchList", trace.Field{Key: "name", Value: r.name}) @@ -788,7 +790,7 @@ func (r *Reflector) watchList(ctx context.Context) (watch.Interface, error) { // we utilize the temporaryStore to ensure independence from the current store implementation. // as of today, the store is implemented as a queue and will be drained by the higher-level // component as soon as it finishes replacing the content. - checkWatchListDataConsistencyIfRequested(ctx, r.name, resourceVersion, r.listerWatcher.ListWithContext, temporaryStore.List) + checkWatchListDataConsistencyIfRequested(ctx, r.name, resourceVersion, r.listerWatcher.ListWithContext, transformer, temporaryStore.List) if err := r.store.Replace(temporaryStore.List(), resourceVersion); err != nil { return nil, fmt.Errorf("unable to sync watch-list result: %w", err) diff --git a/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/reflector_data_consistency_detector.go b/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/reflector_data_consistency_detector.go index a7e0d9c436..4119c78a6c 100644 --- a/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/reflector_data_consistency_detector.go +++ b/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/reflector_data_consistency_detector.go @@ -33,11 +33,11 @@ import ( // // Note that this function will panic when data inconsistency is detected. // This is intentional because we want to catch it in the CI. -func checkWatchListDataConsistencyIfRequested[T runtime.Object, U any](ctx context.Context, identity string, lastSyncedResourceVersion string, listFn consistencydetector.ListFunc[T], retrieveItemsFn consistencydetector.RetrieveItemsFunc[U]) { +func checkWatchListDataConsistencyIfRequested[T runtime.Object, U any](ctx context.Context, identity string, lastSyncedResourceVersion string, listFn consistencydetector.ListFunc[T], listItemTransformFunc func(interface{}) (interface{}, error), retrieveItemsFn consistencydetector.RetrieveItemsFunc[U]) { if !consistencydetector.IsDataConsistencyDetectionForWatchListEnabled() { return } // for informers we pass an empty ListOptions because // listFn might be wrapped for filtering during informer construction. - consistencydetector.CheckDataConsistency(ctx, identity, lastSyncedResourceVersion, listFn, metav1.ListOptions{}, retrieveItemsFn) + consistencydetector.CheckDataConsistency(ctx, identity, lastSyncedResourceVersion, listFn, listItemTransformFunc, metav1.ListOptions{}, retrieveItemsFn) } diff --git a/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/shared_informer.go b/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/shared_informer.go index 99e5fcd180..1c12aa2d64 100644 --- a/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/shared_informer.go +++ b/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/shared_informer.go @@ -539,16 +539,7 @@ func (s *sharedIndexInformer) RunWithContext(ctx context.Context) { s.startedLock.Lock() defer s.startedLock.Unlock() - var fifo Queue - if clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.InOrderInformers) { - fifo = NewRealFIFO(MetaNamespaceKeyFunc, s.indexer, s.transform) - } else { - fifo = NewDeltaFIFOWithOptions(DeltaFIFOOptions{ - KnownObjects: s.indexer, - EmitDeltaTypeReplaced: true, - Transformer: s.transform, - }) - } + fifo := newQueueFIFO(s.indexer, s.transform) cfg := &Config{ Queue: fifo, diff --git a/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/the_real_fifo.go b/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/the_real_fifo.go index ef322bea85..b907410dc0 100644 --- a/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/the_real_fifo.go +++ b/cluster-api/cluster-api/vendor/k8s.io/client-go/tools/cache/the_real_fifo.go @@ -61,7 +61,8 @@ type RealFIFO struct { } var ( - _ = Queue(&RealFIFO{}) // RealFIFO is a Queue + _ = Queue(&RealFIFO{}) // RealFIFO is a Queue + _ = TransformingStore(&RealFIFO{}) // RealFIFO implements TransformingStore to allow memory optimizations ) // Close the queue. diff --git a/cluster-api/cluster-api/vendor/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go b/cluster-api/cluster-api/vendor/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go index 06f172d82b..72c0124a0f 100644 --- a/cluster-api/cluster-api/vendor/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go +++ b/cluster-api/cluster-api/vendor/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go @@ -45,16 +45,28 @@ func IsDataConsistencyDetectionForWatchListEnabled() bool { return dataConsistencyDetectionForWatchListEnabled } +// SetDataConsistencyDetectionForWatchListEnabledForTest allows to enable/disable data consistency detection for testing purposes. +// It returns a function that restores the original value. +func SetDataConsistencyDetectionForWatchListEnabledForTest(enabled bool) func() { + original := dataConsistencyDetectionForWatchListEnabled + dataConsistencyDetectionForWatchListEnabled = enabled + return func() { + dataConsistencyDetectionForWatchListEnabled = original + } +} + type RetrieveItemsFunc[U any] func() []U type ListFunc[T runtime.Object] func(ctx context.Context, options metav1.ListOptions) (T, error) +type TransformFunc func(interface{}) (interface{}, error) + // CheckDataConsistency exists solely for testing purposes. // we cannot use checkWatchListDataConsistencyIfRequested because // it is guarded by an environmental variable. // we cannot manipulate the environmental variable because // it will affect other tests in this package. -func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity string, lastSyncedResourceVersion string, listFn ListFunc[T], listOptions metav1.ListOptions, retrieveItemsFn RetrieveItemsFunc[U]) { +func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity string, lastSyncedResourceVersion string, listFn ListFunc[T], listItemTransformFunc TransformFunc, listOptions metav1.ListOptions, retrieveItemsFn RetrieveItemsFunc[U]) { if !canFormAdditionalListCall(lastSyncedResourceVersion, listOptions) { klog.V(4).Infof("data consistency check for %s is enabled but the parameters (RV, ListOptions) doesn't allow for creating a valid LIST request. Skipping the data consistency check.", identity) return @@ -84,6 +96,15 @@ func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity if err != nil { panic(err) // this should never happen } + if listItemTransformFunc != nil { + for i := range rawListItems { + obj, err := listItemTransformFunc(rawListItems[i]) + if err != nil { + panic(err) + } + rawListItems[i] = obj.(runtime.Object) + } + } listItems := toMetaObjectSliceOrDie(rawListItems) sort.Sort(byUID(listItems)) diff --git a/cluster-api/cluster-api/vendor/modules.txt b/cluster-api/cluster-api/vendor/modules.txt index c7e2ecc3d7..f504a49f36 100644 --- a/cluster-api/cluster-api/vendor/modules.txt +++ b/cluster-api/cluster-api/vendor/modules.txt @@ -164,7 +164,7 @@ github.com/google/go-cmp/cmp/internal/value # github.com/google/uuid v1.6.0 ## explicit github.com/google/uuid -# github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 +# github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 ## explicit; go 1.24.0 github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule github.com/grpc-ecosystem/grpc-gateway/v2/runtime @@ -266,16 +266,17 @@ github.com/x448/float16 ## explicit; go 1.24.0 go.opentelemetry.io/auto/sdk go.opentelemetry.io/auto/sdk/internal/telemetry -# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 -## explicit; go 1.23.0 +# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 +## explicit; go 1.24.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv -# go.opentelemetry.io/otel v1.38.0 -## explicit; go 1.23.0 +# go.opentelemetry.io/otel v1.40.0 +## explicit; go 1.24.0 go.opentelemetry.io/otel go.opentelemetry.io/otel/attribute go.opentelemetry.io/otel/attribute/internal +go.opentelemetry.io/otel/attribute/internal/xxhash go.opentelemetry.io/otel/baggage go.opentelemetry.io/otel/codes go.opentelemetry.io/otel/internal/baggage @@ -283,35 +284,39 @@ go.opentelemetry.io/otel/internal/global go.opentelemetry.io/otel/propagation go.opentelemetry.io/otel/semconv/v1.17.0 go.opentelemetry.io/otel/semconv/v1.37.0 -go.opentelemetry.io/otel/semconv/v1.37.0/httpconv -go.opentelemetry.io/otel/semconv/v1.37.0/otelconv -# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 -## explicit; go 1.23.0 +go.opentelemetry.io/otel/semconv/v1.39.0 +go.opentelemetry.io/otel/semconv/v1.39.0/httpconv +go.opentelemetry.io/otel/semconv/v1.39.0/otelconv +# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 +## explicit; go 1.24.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform -# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 -## explicit; go 1.23.0 +# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 +## explicit; go 1.24.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/envconfig +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry -# go.opentelemetry.io/otel/metric v1.38.0 -## explicit; go 1.23.0 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x +# go.opentelemetry.io/otel/metric v1.40.0 +## explicit; go 1.24.0 go.opentelemetry.io/otel/metric go.opentelemetry.io/otel/metric/embedded go.opentelemetry.io/otel/metric/noop -# go.opentelemetry.io/otel/sdk v1.38.0 -## explicit; go 1.23.0 +# go.opentelemetry.io/otel/sdk v1.40.0 +## explicit; go 1.24.0 go.opentelemetry.io/otel/sdk go.opentelemetry.io/otel/sdk/instrumentation -go.opentelemetry.io/otel/sdk/internal/env go.opentelemetry.io/otel/sdk/internal/x go.opentelemetry.io/otel/sdk/resource go.opentelemetry.io/otel/sdk/trace -go.opentelemetry.io/otel/sdk/trace/internal/x -# go.opentelemetry.io/otel/trace v1.38.0 -## explicit; go 1.23.0 +go.opentelemetry.io/otel/sdk/trace/internal/env +go.opentelemetry.io/otel/sdk/trace/internal/observ +# go.opentelemetry.io/otel/trace v1.40.0 +## explicit; go 1.24.0 go.opentelemetry.io/otel/trace go.opentelemetry.io/otel/trace/embedded go.opentelemetry.io/otel/trace/internal/telemetry @@ -325,7 +330,7 @@ go.opentelemetry.io/proto/otlp/trace/v1 # go.uber.org/multierr v1.11.0 ## explicit; go 1.19 go.uber.org/multierr -# go.uber.org/zap v1.27.0 +# go.uber.org/zap v1.27.1 ## explicit; go 1.19 go.uber.org/zap go.uber.org/zap/buffer @@ -342,7 +347,7 @@ go.yaml.in/yaml/v2 # go.yaml.in/yaml/v3 v3.0.4 ## explicit; go 1.16 go.yaml.in/yaml/v3 -# golang.org/x/crypto v0.44.0 +# golang.org/x/crypto v0.47.0 ## explicit; go 1.24.0 golang.org/x/crypto/bcrypt golang.org/x/crypto/blowfish @@ -351,7 +356,7 @@ golang.org/x/crypto/scrypt # golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 ## explicit; go 1.24.0 golang.org/x/exp/slices -# golang.org/x/net v0.47.0 +# golang.org/x/net v0.49.0 ## explicit; go 1.24.0 golang.org/x/net/html golang.org/x/net/html/atom @@ -364,24 +369,24 @@ golang.org/x/net/internal/httpcommon golang.org/x/net/internal/timeseries golang.org/x/net/trace golang.org/x/net/websocket -# golang.org/x/oauth2 v0.33.0 +# golang.org/x/oauth2 v0.34.0 ## explicit; go 1.24.0 golang.org/x/oauth2 golang.org/x/oauth2/internal -# golang.org/x/sync v0.18.0 +# golang.org/x/sync v0.19.0 ## explicit; go 1.24.0 golang.org/x/sync/errgroup golang.org/x/sync/singleflight -# golang.org/x/sys v0.38.0 +# golang.org/x/sys v0.40.0 ## explicit; go 1.24.0 golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows golang.org/x/sys/windows/registry -# golang.org/x/term v0.37.0 +# golang.org/x/term v0.39.0 ## explicit; go 1.24.0 golang.org/x/term -# golang.org/x/text v0.31.0 +# golang.org/x/text v0.33.0 ## explicit; go 1.24.0 golang.org/x/text/cases golang.org/x/text/encoding @@ -418,15 +423,15 @@ golang.org/x/time/rate # gomodules.xyz/jsonpatch/v2 v2.5.0 ## explicit; go 1.20 gomodules.xyz/jsonpatch/v2 -# google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba +# google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 ## explicit; go 1.24.0 google.golang.org/genproto/googleapis/api/expr/v1alpha1 google.golang.org/genproto/googleapis/api/httpbody -# google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba +# google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 ## explicit; go 1.24.0 google.golang.org/genproto/googleapis/rpc/errdetails google.golang.org/genproto/googleapis/rpc/status -# google.golang.org/grpc v1.77.0 +# google.golang.org/grpc v1.79.3 ## explicit; go 1.24.0 google.golang.org/grpc google.golang.org/grpc/attributes @@ -455,6 +460,7 @@ google.golang.org/grpc/health/grpc_health_v1 google.golang.org/grpc/internal google.golang.org/grpc/internal/backoff google.golang.org/grpc/internal/balancer/gracefulswitch +google.golang.org/grpc/internal/balancer/weight google.golang.org/grpc/internal/balancerload google.golang.org/grpc/internal/binarylog google.golang.org/grpc/internal/buffer @@ -490,7 +496,7 @@ google.golang.org/grpc/serviceconfig google.golang.org/grpc/stats google.golang.org/grpc/status google.golang.org/grpc/tap -# google.golang.org/protobuf v1.36.10 +# google.golang.org/protobuf v1.36.11 ## explicit; go 1.23 google.golang.org/protobuf/encoding/protodelim google.golang.org/protobuf/encoding/protojson @@ -541,7 +547,7 @@ gopkg.in/evanphx/json-patch.v4 # gopkg.in/inf.v0 v0.9.1 ## explicit gopkg.in/inf.v0 -# k8s.io/api v0.34.2 +# k8s.io/api v0.34.3 ## explicit; go 1.24.0 k8s.io/api/admission/v1 k8s.io/api/admission/v1beta1 @@ -602,7 +608,7 @@ k8s.io/api/storage/v1 k8s.io/api/storage/v1alpha1 k8s.io/api/storage/v1beta1 k8s.io/api/storagemigration/v1alpha1 -# k8s.io/apiextensions-apiserver v0.34.2 +# k8s.io/apiextensions-apiserver v0.34.3 ## explicit; go 1.24.0 k8s.io/apiextensions-apiserver/pkg/apihelpers k8s.io/apiextensions-apiserver/pkg/apis/apiextensions @@ -617,7 +623,7 @@ k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning k8s.io/apiextensions-apiserver/pkg/apiserver/validation k8s.io/apiextensions-apiserver/pkg/features -# k8s.io/apimachinery v0.34.2 +# k8s.io/apimachinery v0.34.3 ## explicit; go 1.24.0 k8s.io/apimachinery/pkg/api/apitesting k8s.io/apimachinery/pkg/api/apitesting/fuzzer @@ -688,7 +694,7 @@ k8s.io/apimachinery/pkg/version k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/apiserver v0.34.2 +# k8s.io/apiserver v0.34.3 ## explicit; go 1.24.0 k8s.io/apiserver/pkg/admission k8s.io/apiserver/pkg/apis/apiserver @@ -753,7 +759,7 @@ k8s.io/apiserver/pkg/warning k8s.io/apiserver/plugin/pkg/authenticator/token/webhook k8s.io/apiserver/plugin/pkg/authorizer/webhook k8s.io/apiserver/plugin/pkg/authorizer/webhook/metrics -# k8s.io/client-go v0.34.2 +# k8s.io/client-go v0.34.3 ## explicit; go 1.24.0 k8s.io/client-go/applyconfigurations/admissionregistration/v1 k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1 @@ -1033,7 +1039,7 @@ k8s.io/client-go/util/workqueue ## explicit; go 1.24.0 k8s.io/cluster-bootstrap/token/api k8s.io/cluster-bootstrap/token/util -# k8s.io/component-base v0.34.2 +# k8s.io/component-base v0.34.3 ## explicit; go 1.24.0 k8s.io/component-base/cli/flag k8s.io/component-base/compatibility @@ -1106,12 +1112,14 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/metrics sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/common/metrics sigs.k8s.io/apiserver-network-proxy/konnectivity-client/proto/client -# sigs.k8s.io/cluster-api v1.11.3 +# sigs.k8s.io/cluster-api v1.12.8 ## explicit; go 1.24.0 sigs.k8s.io/cluster-api sigs.k8s.io/cluster-api/api/addons/v1beta1 sigs.k8s.io/cluster-api/api/addons/v1beta2 +sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1 sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2 +sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1 sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2 sigs.k8s.io/cluster-api/api/core/v1beta1 sigs.k8s.io/cluster-api/api/core/v1beta2 @@ -1129,20 +1137,11 @@ sigs.k8s.io/cluster-api/controllers/external sigs.k8s.io/cluster-api/controllers/noderefutil sigs.k8s.io/cluster-api/controllers/remote sigs.k8s.io/cluster-api/errors -sigs.k8s.io/cluster-api/exp/controllers -sigs.k8s.io/cluster-api/exp/internal/controllers -sigs.k8s.io/cluster-api/exp/internal/webhooks -sigs.k8s.io/cluster-api/exp/ipam/internal/webhooks -sigs.k8s.io/cluster-api/exp/ipam/webhooks sigs.k8s.io/cluster-api/exp/runtime/catalog sigs.k8s.io/cluster-api/exp/runtime/client -sigs.k8s.io/cluster-api/exp/runtime/controllers -sigs.k8s.io/cluster-api/exp/runtime/internal/controllers sigs.k8s.io/cluster-api/exp/runtime/topologymutation sigs.k8s.io/cluster-api/exp/topology/desiredstate sigs.k8s.io/cluster-api/exp/topology/scope -sigs.k8s.io/cluster-api/exp/util -sigs.k8s.io/cluster-api/exp/webhooks sigs.k8s.io/cluster-api/feature sigs.k8s.io/cluster-api/internal/api/addons/v1alpha3 sigs.k8s.io/cluster-api/internal/api/addons/v1alpha4 @@ -1154,11 +1153,13 @@ sigs.k8s.io/cluster-api/internal/controllers/clusterclass sigs.k8s.io/cluster-api/internal/controllers/clusterresourceset sigs.k8s.io/cluster-api/internal/controllers/clusterresourceset/predicates sigs.k8s.io/cluster-api/internal/controllers/clusterresourcesetbinding +sigs.k8s.io/cluster-api/internal/controllers/extensionconfig sigs.k8s.io/cluster-api/internal/controllers/machine sigs.k8s.io/cluster-api/internal/controllers/machine/drain sigs.k8s.io/cluster-api/internal/controllers/machinedeployment sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil sigs.k8s.io/cluster-api/internal/controllers/machinehealthcheck +sigs.k8s.io/cluster-api/internal/controllers/machinepool sigs.k8s.io/cluster-api/internal/controllers/machineset sigs.k8s.io/cluster-api/internal/controllers/topology/cluster sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches @@ -1179,12 +1180,15 @@ sigs.k8s.io/cluster-api/internal/topology/names sigs.k8s.io/cluster-api/internal/topology/ownerrefs sigs.k8s.io/cluster-api/internal/topology/selectors sigs.k8s.io/cluster-api/internal/topology/variables +sigs.k8s.io/cluster-api/internal/util/client sigs.k8s.io/cluster-api/internal/util/compare +sigs.k8s.io/cluster-api/internal/util/controller sigs.k8s.io/cluster-api/internal/util/hash +sigs.k8s.io/cluster-api/internal/util/inplace +sigs.k8s.io/cluster-api/internal/util/patch sigs.k8s.io/cluster-api/internal/util/ssa sigs.k8s.io/cluster-api/internal/util/taints sigs.k8s.io/cluster-api/internal/webhooks -sigs.k8s.io/cluster-api/internal/webhooks/runtime sigs.k8s.io/cluster-api/util sigs.k8s.io/cluster-api/util/annotations sigs.k8s.io/cluster-api/util/apiwarnings @@ -1210,7 +1214,7 @@ sigs.k8s.io/cluster-api/util/version sigs.k8s.io/cluster-api/util/yaml sigs.k8s.io/cluster-api/version sigs.k8s.io/cluster-api/webhooks -# sigs.k8s.io/controller-runtime v0.22.4 +# sigs.k8s.io/controller-runtime v0.22.5 ## explicit; go 1.24.0 sigs.k8s.io/controller-runtime sigs.k8s.io/controller-runtime/pkg/builder @@ -1265,7 +1269,7 @@ sigs.k8s.io/json/internal/golang/encoding/json ## explicit; go 1.18 sigs.k8s.io/randfill sigs.k8s.io/randfill/bytesource -# sigs.k8s.io/structured-merge-diff/v6 v6.3.0 +# sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 ## explicit; go 1.23 sigs.k8s.io/structured-merge-diff/v6/fieldpath sigs.k8s.io/structured-merge-diff/v6/merge diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.dockerignore b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.dockerignore index f5796e7a7b..382a57f0cc 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.dockerignore +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.dockerignore @@ -54,3 +54,5 @@ go.work.sum **/*.tmp **/.DS_Store **/*.swp + +tmp/ diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.gitignore b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.gitignore index 78da1a3036..a784fa5d53 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.gitignore +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.gitignore @@ -12,13 +12,13 @@ hack/tools/bin # E2E test templates test/e2e/data/infrastructure-docker/**/cluster-template*.yaml -!test/e2e/data/infrastructure-docker/**/clusterclass-quick-start.yaml !test/e2e/data/infrastructure-docker/**/clusterclass-quick-start-runtimesdk.yaml !test/e2e/data/infrastructure-docker/**/clusterclass-quick-start-runtimesdk-v1beta1.yaml !test/e2e/data/infrastructure-docker/**/cluster-template-in-memory.yaml !test/e2e/data/infrastructure-docker/**/clusterclass-in-memory.yaml test/e2e/data/infrastructure-docker/**/clusterclass-*.yaml test/e2e/data/infrastructure-inmemory/**/cluster-template*.yaml +!test/e2e/data/infrastructure-docker/**/clusterclass-quick-start.yaml # Output of Makefile targets using sed on MacOS systems *.yaml-e @@ -71,7 +71,7 @@ vendor # User-supplied Tiltfile extensions, settings, and builds tilt.d tilt-settings.json -tilt-settings.yaml +tilt-settings.*yaml .tiltbuild # User-supplied clusterctl hacks settings diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.golangci-kal.yml b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.golangci-kal.yml index bae5742288..eea0229865 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.golangci-kal.yml +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.golangci-kal.yml @@ -18,12 +18,15 @@ linters: - "conditions" # Ensure conditions have the correct json tags and markers. - "conflictingmarkers" - "duplicatemarkers" # Ensure there are no exact duplicate markers. for types and fields. + - "forbiddenmarkers" # Ensure that types and fields do not contain any markers that are forbidden. - "integers" # Ensure only int32 and int64 are used for integers. - "jsontags" # Ensure every field has a json tag. - "maxlength" # Ensure all strings and arrays have maximum lengths/maximum items. - "nobools" # Bools do not evolve over time, should use enums instead. + - "nodurations" # Prevents usage of `Duration` types. - "nofloats" # Ensure floats are not used. - "nomaps" # Ensure maps are not used. + - "nonullable" # Ensure that types and fields do not have the nullable marker. - "notimestamp" # Prevents usage of 'Timestamp' fields - "optionalfields" # Ensure that all fields marked as optional adhere to being pointers and # having the `omitempty` value in their `json` tag where appropriate. @@ -50,6 +53,12 @@ linters: - ["default", "kubebuilder:default"] - ["required", "kubebuilder:validation:Required", "k8s:required"] description: "A field with a default value cannot be required" + forbiddenmarkers: + markers: + # We don't want to do any defaulting (including OpenAPI) anymore on API fields because we prefer + # to have a clear signal on user intent. This also allows us to easily change the default behavior if necessary. + - identifier: "kubebuilder:default" + - identifier: "default" conditions: isFirstField: Warn # Require conditions to be the first field in the status struct. usePatchStrategy: Forbid # Forbid patchStrategy markers on the Conditions field. @@ -83,15 +92,15 @@ linters: ## Excludes for current apiVersions that can be removed once v1beta1 is removed. # .status.deprecated.v1beta1.conditions fields are using v1beta1.Condition types. - path: "api/addons/v1beta2|api/bootstrap/kubeadm/v1beta2|api/controlplane/kubeadm/v1beta2|api/core/v1beta2|api/ipam/v1beta2|api/runtime/v1beta2" - text: "Conditions field must be a slice of metav1.Condition" + text: "Conditions field in .*V1Beta1DeprecatedStatus must be a slice of metav1.Condition" linters: - kubeapilinter - path: "api/addons/v1beta2|api/bootstrap/kubeadm/v1beta2|api/controlplane/kubeadm/v1beta2|api/core/v1beta2|api/ipam/v1beta2|api/runtime/v1beta2" - text: "ssatags: Conditions should have a listType marker for proper Server-Side Apply behavior" + text: "ssatags: .*Conditions should have a listType marker for proper Server-Side Apply behavior" linters: - kubeapilinter - path: "api/core/v1beta2" - text: "field Conditions type Conditions must have a maximum items, add kubebuilder:validation:MaxItems marker" + text: "field .*Conditions type Conditions must have a maximum items, add kubebuilder:validation:MaxItems marker" linters: - kubeapilinter - path: "api/core/v1beta2/condition_types.go" @@ -102,24 +111,18 @@ linters: ## Excludes for current clusterctl v1alpha3 and Runtime Hooks v1alpha1 apiVersions (can be fixed once we bump their apiVersion). # Note: The types in api/runtime/hooks/v1alpha1 are not CRDs, so e.g. SSA markers don't make sense there. - path: "cmd/clusterctl/api/v1alpha3|api/runtime/hooks/v1alpha1" - text: "optionalfields|requiredfields|maxlength|ssatags" + text: "maxlength|ssatags" linters: - kubeapilinter - - ## Excludes for JSONSchemaProps - # controller-gen does not allow to add MaxItems to Schemaless fields: https://github.com/kubernetes-sigs/kube-api-linter/issues/120 - - path: "api/core/v1beta2/clusterclass_types.go" - text: "maxlength: field (AllOf|OneOf|AnyOf) must have a maximum items, add kubebuilder:validation:MaxItems marker" - linters: - - kubeapilinter - # controller-gen does not allow to add listType to Schemaless fields: https://github.com/kubernetes-sigs/kube-api-linter/issues/120 - - path: "api/core/v1beta2/clusterclass_types.go" - text: "ssatags: (AllOf|OneOf|AnyOf) should have a listType marker for proper Server-Side Apply behavior" + - path: "cmd/clusterctl/api/v1alpha3|api/runtime/hooks/v1alpha1/(common_types.go|discovery_types.go|lifecyclehooks_types.go|topologymutation_types.go|topologymutation_variable_types.go)" + text: "optionalfields|requiredfields" linters: - kubeapilinter + + ## Excludes for JSONSchemaProps # We want to align to the JSON tags of the CustomResourceDefinition fields. - path: "api/core/v1beta2/clusterclass_types" - text: "field (XPreserveUnknownFields|XPreserveUnknownFields|XValidations|XMetadata|XIntOrString) json tag does not match pattern" + text: "field JSONSchemaProps.(XPreserveUnknownFields|XPreserveUnknownFields|XValidations|XMetadata|XIntOrString) json tag does not match pattern" linters: - kubeapilinter # We want to align Properties to the corresponding field in CustomResourceDefinitions. @@ -142,7 +145,7 @@ linters: ## Excludes for kubeadm types # We want to align the FeatureGates field to the FeatureGates field in kubeadm. - path: "api/bootstrap/kubeadm/v1beta2/kubeadm_types.go" - text: "nomaps: FeatureGates should not use a map type, use a list type with a unique name/identifier instead" + text: "nomaps: ClusterConfiguration.FeatureGates should not use a map type, use a list type with a unique name/identifier instead" linters: - kubeapilinter @@ -161,6 +164,16 @@ linters: linters: - kubeapilinter + # Excludes for existing default markers + - path: "api/core/v1beta2/clusterclass_types.go" + text: 'forbiddenmarkers: field ValidationRule.Reason has forbidden marker "kubebuilder:default=FieldValueInvalid"' + linters: + - kubeapilinter + - path: "api/core/v1beta2/clusterclass_types.go" + text: 'forbiddenmarkers: field ValidationRule.Reason has forbidden marker "default=ref\(sigs.k8s.io/cluster-api/api/core/v1beta2.FieldValueInvalid\)"' + linters: + - kubeapilinter + # TODO: Excludes that should be removed once the corresponding issues in KAL are fixed # KAL incorrectly reports that the Taints field doesn't have to be a pointer (it has to be to preserve []). # See: https://github.com/kubernetes-sigs/kube-api-linter/issues/116 diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.golangci.yml b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.golangci.yml index bc313cbadf..4d7e78db2b 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.golangci.yml +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.golangci.yml @@ -20,9 +20,11 @@ linters: - durationcheck # multiplying two durations - errcheck # unchecked errors - errchkjson # invalid types passed to json encoder + - forbidigo # allows to block usage of funcs - ginkgolinter # ginkgo and gomega - gocritic # bugs, performance, style (we could add custom ones to this one) - godot # checks that comments end in a period + - godox # block FIXMEs - goprintffuncname # printft-like functions should be named with f at the end - gosec # potential security problems - govet # basically 'go vet' @@ -51,6 +53,10 @@ linters: # TODO: It will be dropped when the Go version migration is done. - usetesting settings: + forbidigo: + forbid: + - pattern: ctrl.NewControllerManagedBy + msg: Use capicontrollerutil.NewControllerManagedBy instead ginkgolinter: forbid-focus-container: true gocritic: @@ -83,6 +89,9 @@ linters: - ^ \+.* - ^ ANCHOR.* - '^ (alpha|beta|GA): v.*' + godox: + keywords: + - FIXME # FIXME's should be removed before merging PRs gosec: excludes: # integer overflow conversion int -> int32 @@ -174,6 +183,10 @@ linters: alias: "" - pkg: sigs.k8s.io/cluster-api/internal/topology/names alias: topologynames + - pkg: sigs.k8s.io/cluster-api/internal/util/client + alias: "clientutil" + - pkg: sigs.k8s.io/cluster-api/internal/util/controller + alias: "capicontrollerutil" # CAPD - pkg: sigs.k8s.io/cluster-api/test/infrastructure/docker/api/v1alpha3 alias: infrav1alpha3 @@ -257,19 +270,11 @@ linters: - linters: - staticcheck text: 'SA1019: (clusterv1alpha3.*|clusterv1alpha4.*) is deprecated: This type will be removed in one of the next releases.' - # Specific exclude rules for deprecated feature flags - - linters: - - staticcheck - text: 'SA1019: feature.ClusterResourceSet is deprecated: ClusterResourceSet feature is now GA and the corresponding feature flag will be removed in 1.12 release.' # v1Beta1 deprecated fields - linters: - staticcheck text: 'SA1019: .*\.Deprecated\.V1Beta1.* is deprecated' - # CR v0.21 deprecated Result.Requeue, will be fixed incrementally and tracked via https://github.com/kubernetes-sigs/cluster-api/issues/12272 - - linters: - - staticcheck - text: 'SA1019: .*(res|result|i|j)\.Requeue is deprecated: Use `RequeueAfter` instead' - # TODO: var-naming: avoid meaningless package names by revive + # TODO: var-naming: avoid meaningless package names by revive # * test/infrastructure/docker/internal/docker/types/ # * bootstrap/kubeadm/types/ # * internal/webhooks/util/ @@ -402,6 +407,22 @@ linters: - staticcheck path: (.+)\.go$ text: 'QF1008: could remove embedded field' + - linters: + - revive + path: errors/.*\.go$ + text: 'var-naming: avoid package names that conflict with Go standard library package names' + - linters: + - revive + path: internal/util/hash/.*\.go$ + text: 'var-naming: avoid package names that conflict with Go standard library package names' + - linters: + - revive + path: internal/controllers/topology/cluster/patches/api/.*\.go$ + text: 'var-naming: avoid meaningless package names' + - linters: + - revive + path: test/infrastructure/inmemory/pkg/server/api/.*\.go$ + text: 'var-naming: avoid meaningless package names' issues: max-issues-per-linter: 0 max-same-issues: 0 diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.govuln_exclude b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.govuln_exclude new file mode 100644 index 0000000000..4c4dc35d94 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.govuln_exclude @@ -0,0 +1,2 @@ +# GO-2026-4918: This should not be an issue as KCP only uses the affected code to communicate with the kube-apiserver. +GO-2026-4918 diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.trivyignore b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.trivyignore new file mode 100644 index 0000000000..a37603a281 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/.trivyignore @@ -0,0 +1,8 @@ +# CVE-2026-39883: opentelemetry-go BSD kenv command PATH hijacking +# This vulnerability only affects BSD/Solaris platforms. +# Cluster API runs on Linux containers, so this CVE is not exploitable. +# The fix (otel/sdk v1.43.0) requires Go 1.25, which is not available on this release branch. +# govulncheck confirms no vulnerable code path is reachable. +CVE-2026-39883 +# CVE-2026-29181: Cluster API is not using the affected otel packages (verified via govulncheck) +CVE-2026-29181 diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/CONTRIBUTING.md b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/CONTRIBUTING.md index 636f40a384..b9b5489b4e 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/CONTRIBUTING.md +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/CONTRIBUTING.md @@ -75,10 +75,8 @@ Cluster API follows upstream Kubernetes semantic versioning. With the v1 release - A (*patch*) release SHOULD only include backwards compatible set of bugfixes. -These guarantees extend to all code exposed in our Go Module, including -*types from dependencies in public APIs*. -Types and functions not in public APIs are not considered part of the guarantee. -The test module, clusterctl, and experiments do not provide any backward compatible guarantees. +see [Cluster API release support](https://cluster-api.sigs.k8s.io/reference/versions.html#cluster-api-release-support) for +more details about supported releases and for considerations that might apply if you are importing Cluster API go modules as a dependency. #### Backporting a patch @@ -88,7 +86,7 @@ Any backport MUST NOT be breaking for API or behavioral changes. We usually backport critical bugs or security fixes, changes to support new Kubernetes minor versions (see [supported Kubernetes versions](https://cluster-api.sigs.k8s.io/reference/versions.html#supported-kubernetes-versions)), documentation and test signal improvements. Everything else is considered case by case. Release branches outside of the [standard support period](https://github.com/kubernetes-sigs/cluster-api/blob/main/CONTRIBUTING.md#cluster-api-release-support) are usually frozen, -although maintainers may allow backports to releases in [maintenance mode](https://github.com/kubernetes-sigs/cluster-api/blob/main/CONTRIBUTING.md#cluster-api-release-support) in specific situations +although maintainers may allow backports to releases in [maintenance mode](https://github.com/kubernetes-sigs/cluster-api/blob/main/CONTRIBUTING.md#cluster-api-release-support) in specific situations like CVEs, security, and other critical bug fixes. ### APIs @@ -379,7 +377,7 @@ licenses dependencies and other artifacts use. For go dependencies only dependen This project follows the [Kubernetes API conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md). We enforce the API conventions via [kube-api-linter](https://github.com/kubernetes-sigs/kube-api-linter). -The corresponding configuration field can be found [here](https://github.com/kubernetes-sigs/cluster-api/blob/main/.golangci-kal.yml). +The corresponding configuration field can be found [here](https://github.com/kubernetes-sigs/cluster-api/blob/main/.golangci-kal.yml). Minor additions to the conventions are listed below. @@ -460,3 +458,5 @@ As of today there are following OWNERS files/Owner groups defining sub areas: - [Test](https://github.com/kubernetes-sigs/cluster-api/tree/main/test) - [Test Framework](https://github.com/kubernetes-sigs/cluster-api/tree/main/test/framework) - [Docs](https://github.com/kubernetes-sigs/cluster-api/tree/main/docs) +- [Machine pools](https://github.com/kubernetes-sigs/cluster-api/tree/main/internal/controllers/machinepool) +- Ignition support [in kubeadm Bootstrap Provider](https://github.com/kubernetes-sigs/cluster-api/tree/main/bootstrap/kubeadm/internal/ignition) and [in CAPD](https://github.com/kubernetes-sigs/cluster-api/tree/main/test/infrastructure/docker/internal/provisioning/ignition) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/Makefile b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/Makefile index 83270cd4a2..519650c228 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/Makefile +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/Makefile @@ -23,7 +23,7 @@ SHELL:=/usr/bin/env bash # # Go. # -GO_VERSION ?= 1.24.9 +GO_VERSION ?= 1.25.10 GO_DIRECTIVE_VERSION ?= 1.24.0 GO_CONTAINER_IMAGE ?= docker.io/library/golang:$(GO_VERSION) @@ -44,7 +44,7 @@ export GO111MODULE=on # # Kubebuilder. # -export KUBEBUILDER_ENVTEST_KUBERNETES_VERSION ?= 1.34.0 +export KUBEBUILDER_ENVTEST_KUBERNETES_VERSION ?= 1.35.0 export KUBEBUILDER_CONTROLPLANE_START_TIMEOUT ?= 60s export KUBEBUILDER_CONTROLPLANE_STOP_TIMEOUT ?= 60s @@ -106,12 +106,12 @@ KUSTOMIZE_BIN := kustomize KUSTOMIZE := $(abspath $(TOOLS_BIN_DIR)/$(KUSTOMIZE_BIN)-$(KUSTOMIZE_VER)) KUSTOMIZE_PKG := sigs.k8s.io/kustomize/kustomize/v5 -SETUP_ENVTEST_VER := release-0.21 +SETUP_ENVTEST_VER := release-0.22 SETUP_ENVTEST_BIN := setup-envtest SETUP_ENVTEST := $(abspath $(TOOLS_BIN_DIR)/$(SETUP_ENVTEST_BIN)-$(SETUP_ENVTEST_VER)) SETUP_ENVTEST_PKG := sigs.k8s.io/controller-runtime/tools/setup-envtest -CONTROLLER_GEN_VER := v0.18.0 +CONTROLLER_GEN_VER := v0.19.0 CONTROLLER_GEN_BIN := controller-gen CONTROLLER_GEN := $(abspath $(TOOLS_BIN_DIR)/$(CONTROLLER_GEN_BIN)-$(CONTROLLER_GEN_VER)) CONTROLLER_GEN_PKG := sigs.k8s.io/controller-tools/cmd/controller-gen @@ -121,7 +121,7 @@ GOTESTSUM_BIN := gotestsum GOTESTSUM := $(abspath $(TOOLS_BIN_DIR)/$(GOTESTSUM_BIN)-$(GOTESTSUM_VER)) GOTESTSUM_PKG := gotest.tools/gotestsum -CONVERSION_GEN_VER := v0.33.0 +CONVERSION_GEN_VER := v0.34.0 CONVERSION_GEN_BIN := conversion-gen # We are intentionally using the binary without version suffix, to avoid the version # in generated files. @@ -138,7 +138,7 @@ HADOLINT_FAILURE_THRESHOLD = warning SHELLCHECK_VER := v0.10.0 -TRIVY_VER := 0.64.0 +TRIVY_VER := 0.69.2 KPROMO_VER := 5ab0dbc74b0228c22a93d240596dff77464aee8f KPROMO_BIN := kpromo @@ -168,9 +168,15 @@ GOLANGCI_LINT_KAL_VER := $(shell cat ./hack/tools/.custom-gcl.yaml | grep versio GOLANGCI_LINT_KAL := $(abspath $(TOOLS_BIN_DIR)/$(GOLANGCI_LINT_KAL_BIN)) GOVULNCHECK_BIN := govulncheck -GOVULNCHECK_VER := v1.1.4 -GOVULNCHECK := $(abspath $(TOOLS_BIN_DIR)/$(GOVULNCHECK_BIN)-$(GOVULNCHECK_VER)) -GOVULNCHECK_PKG := golang.org/x/vuln/cmd/govulncheck +GOVULNCHECK_VER := v1.3.0 +GOVULNCHECK := $(abspath $(TOOLS_BIN_DIR)/$(GOVULNCHECK_BIN)) +GOVULNCHECK_DIR := hack/tools/govulncheck +GOVULNCHECK_TMP_DIR ?= $(GOVULNCHECK_DIR)/govulncheck.tmp + +CRANE_BIN := crane +CRANE_VER := v0.20.7 +CRANE := $(abspath $(TOOLS_BIN_DIR)/$(CRANE_BIN)-$(CRANE_VER)) +CRANE_PKG := github.com/google/go-containerregistry/cmd/crane IMPORT_BOSS_BIN := import-boss IMPORT_BOSS_VER := v0.28.1 @@ -289,10 +295,6 @@ generate-manifests-core: $(CONTROLLER_GEN) $(KUSTOMIZE) ## Generate manifests e. paths=./internal/controllers/... \ paths=./internal/webhooks/... \ paths=./internal/api/addons/... \ - paths=./exp/internal/controllers/... \ - paths=./exp/internal/webhooks/... \ - paths=./exp/ipam/internal/webhooks/... \ - paths=./exp/runtime/internal/controllers/... \ crd:crdVersions=v1 \ rbac:roleName=manager-role \ output:crd:dir=./config/crd/bases \ @@ -374,9 +376,6 @@ generate-manifests-docker-infrastructure: $(CONTROLLER_GEN) ## Generate manifest cd $(CAPD_DIR); $(CONTROLLER_GEN) \ paths=./ \ paths=./api/... \ - paths=./exp/api/... \ - paths=./exp/internal/controllers/... \ - paths=./exp/internal/webhooks/... \ paths=./internal/controllers/... \ paths=./internal/webhooks/... \ crd:crdVersions=v1 \ @@ -434,11 +433,10 @@ generate-go-deepcopy-kubeadm-control-plane: $(CONTROLLER_GEN) ## Generate deepco .PHONY: generate-go-deepcopy-docker-infrastructure generate-go-deepcopy-docker-infrastructure: $(CONTROLLER_GEN) generate-go-deepcopy-in-memory-infrastructure ## Generate deepcopy go code for docker infrastructure provider - $(MAKE) clean-generated-deepcopy SRC_DIRS="$(CAPD_DIR)/api,$(CAPD_DIR)/exp/api" + $(MAKE) clean-generated-deepcopy SRC_DIRS="$(CAPD_DIR)/api" cd $(CAPD_DIR); $(CONTROLLER_GEN) \ object:headerFile=../../../hack/boilerplate/boilerplate.generatego.txt \ - paths=./api/... \ - paths=./exp/api/... + paths=./api/... .PHONY: generate-go-deepcopy-in-memory-infrastructure generate-go-deepcopy-in-memory-infrastructure: $(CONTROLLER_GEN) ## Generate deepcopy go code for in-memory cloud resources @@ -483,7 +481,7 @@ generate-go-conversions-addons-api: $(CONVERSION_GEN) ## Generate conversions go ./api/addons/v1beta1 .PHONY: generate-go-conversions-core-ipam -generate-go-conversions-core-ipam: $(CONVERSION_GEN) ## Generate conversions go code for core exp IPAM +generate-go-conversions-core-ipam: $(CONVERSION_GEN) ## Generate conversions go code for IPAM $(MAKE) clean-generated-conversions SRC_DIRS="./api/ipam/v1beta1,./api/ipam/v1alpha1" $(CONVERSION_GEN) \ --output-file=zz_generated.conversion.go \ @@ -538,10 +536,7 @@ generate-go-conversions-docker-infrastructure: $(CONVERSION_GEN) ## Generate con --go-header-file=../../../hack/boilerplate/boilerplate.generatego.txt \ ./api/v1alpha3 \ ./api/v1alpha4 \ - ./api/v1beta1 \ - ./exp/api/v1alpha3 \ - ./exp/api/v1alpha4 \ - ./exp/api/v1beta1 + ./api/v1beta1 .PHONY: generate-go-conversions-test-extension generate-go-conversions-test-extension: $(CONVERSION_GEN) ## Generate conversions go code for test runtime extension provider @@ -573,7 +568,7 @@ generate-doctoc: TRACE=$(TRACE) ./hack/generate-doctoc.sh .PHONY: generate-e2e-templates -generate-e2e-templates: $(KUSTOMIZE) $(addprefix generate-e2e-templates-, v0.3 v0.4 v1.5 v1.6 v1.8 v1.9 v1.10 main) ## Generate cluster templates for all versions +generate-e2e-templates: $(KUSTOMIZE) $(addprefix generate-e2e-templates-, v0.3 v0.4 v1.5 v1.6 v1.9 v1.10 v1.11 main) ## Generate cluster templates for all versions DOCKER_TEMPLATES := test/e2e/data/infrastructure-docker @@ -595,11 +590,6 @@ generate-e2e-templates-v1.6: $(KUSTOMIZE) $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.6/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.6/cluster-template.yaml $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.6/cluster-template-topology --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.6/cluster-template-topology.yaml -.PHONY: generate-e2e-templates-v1.8 -generate-e2e-templates-v1.8: $(KUSTOMIZE) - $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.8/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.8/cluster-template.yaml - $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.8/cluster-template-topology --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.8/cluster-template-topology.yaml - .PHONY: generate-e2e-templates-v1.9 generate-e2e-templates-v1.9: $(KUSTOMIZE) $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.9/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.9/cluster-template.yaml @@ -610,10 +600,16 @@ generate-e2e-templates-v1.10: $(KUSTOMIZE) $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.10/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.10/cluster-template.yaml $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.10/cluster-template-topology --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.10/cluster-template-topology.yaml +.PHONY: generate-e2e-templates-v1.11 +generate-e2e-templates-v1.11: $(KUSTOMIZE) + $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.11/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.11/cluster-template.yaml + $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/v1.11/cluster-template-topology --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/v1.11/cluster-template-topology.yaml + .PHONY: generate-e2e-templates-main generate-e2e-templates-main: $(KUSTOMIZE) $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template.yaml $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-md-remediation --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-md-remediation.yaml + $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-md-taints --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-md-taints.yaml $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-kcp-remediation --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-kcp-remediation.yaml $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-kcp-adoption/step1 --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-kcp-adoption.yaml echo "---" >> $(DOCKER_TEMPLATES)/main/cluster-template-kcp-adoption.yaml @@ -626,6 +622,7 @@ generate-e2e-templates-main: $(KUSTOMIZE) $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-ipv6 --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-ipv6.yaml $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-topology-dualstack-ipv6-primary --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-topology-dualstack-ipv6-primary.yaml $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-topology-dualstack-ipv4-primary --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-topology-dualstack-ipv4-primary.yaml + $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-topology-in-place --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-topology-in-place.yaml $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-topology-no-workers --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-topology-no-workers.yaml $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-topology-runtimesdk-v1beta1 --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-topology-runtimesdk-v1beta1.yaml $(KUSTOMIZE) build $(DOCKER_TEMPLATES)/main/cluster-template-topology-kcp-only --load-restrictor LoadRestrictionsNone > $(DOCKER_TEMPLATES)/main/cluster-template-topology-kcp-only.yaml @@ -733,7 +730,6 @@ verify-conversions: $(CONVERSION_VERIFIER) ## Verifies expected API conversion ./api/... \ ./internal/api/... \ ./test/infrastructure/docker/api/... \ - ./test/infrastructure/docker/exp/api/... .PHONY: verify-doctoc verify-doctoc: generate-doctoc @@ -950,30 +946,38 @@ test-cover: ## Run unit and integration tests and generate a coverage report go tool cover -func=out/coverage.out -o out/coverage.txt go tool cover -html=out/coverage.out -o out/coverage.html -.PHONY: test-docker-infrastructure -test-docker-infrastructure: $(SETUP_ENVTEST) ## Run unit and integration tests with race detector for docker infrastructure provider +.PHONY: test-infrastructure +test-infrastructure: $(SETUP_ENVTEST) ## Run unit and integration tests with race detector for docker infrastructure provider # Note: Fuzz tests are not executed with race detector because they would just time out. # To achieve that, all files with fuzz tests have the "!race" build tag, to still run fuzz tests # we have an additional `go test` run that focuses on "TestFuzzyConversion". - cd $(CAPD_DIR); KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test -race ./... $(TEST_ARGS) - $(MAKE) test-docker-infrastructure-conversions TEST_ARGS="$(TEST_ARGS)" + cd test/infrastructure; KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test -race ./... $(TEST_ARGS) + $(MAKE) test-infrastructure-conversions TEST_ARGS="$(TEST_ARGS)" -.PHONY: test-docker-infrastructure-conversions -test-docker-infrastructure-conversions: $(SETUP_ENVTEST) ## Run conversions test for docker infrastructure provider - cd $(CAPD_DIR); KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test -run "^TestFuzzyConversion$$" ./... $(TEST_ARGS) +.PHONY: test-infrastructure-no-race +test-infrastructure-no-race: $(SETUP_ENVTEST) ## Run unit and integration tests with no race detector for docker infrastructure provider + # Note: Fuzz tests are not executed with race detector because they would just time out. + # To achieve that, all files with fuzz tests have the "!race" build tag, to still run fuzz tests + # we have an additional `go test` run that focuses on "TestFuzzyConversion". + cd test/infrastructure; KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test ./... $(TEST_ARGS) + +.PHONY: test-infrastructure-conversions +test-infrastructure-conversions: $(SETUP_ENVTEST) ## Run conversions test for docker infrastructure provider + cd test/infrastructure; KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test -run "^TestFuzzyConversion$$" ./... $(TEST_ARGS) -.PHONY: test-docker-infrastructure-verbose -test-docker-infrastructure-verbose: ## Run unit and integration tests with race detector and with verbose flag for docker infrastructure provider - $(MAKE) test-docker-infrastructure TEST_ARGS="$(TEST_ARGS) -v" +.PHONY: test-infrastructure-verbose +test-infrastructure-verbose: ## Run unit and integration tests with race detector and with verbose flag for docker infrastructure provider + $(MAKE) test-infrastructure TEST_ARGS="$(TEST_ARGS) -v" -.PHONY: test-docker-infrastructure-junit -test-docker-infrastructure-junit: $(SETUP_ENVTEST) $(GOTESTSUM) ## Run unit and integration tests with race detector and generate a junit report for docker infrastructure provider - cd $(CAPD_DIR); set +o errexit; (KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test -race -json ./... $(TEST_ARGS); echo $$? > $(ARTIFACTS)/junit.infra_docker.exitcode) | tee $(ARTIFACTS)/junit.infra_docker.stdout +.PHONY: test-infrastructure-junit +test-infrastructure-junit: $(SETUP_ENVTEST) $(GOTESTSUM) ## Run unit and integration tests with race detector and generate a junit report for docker infrastructure provider + cd test/infrastructure; set +o errexit; (KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test -race -json ./... $(TEST_ARGS); echo $$? > $(ARTIFACTS)/junit.infra_docker.exitcode) | tee $(ARTIFACTS)/junit.infra_docker.stdout $(GOTESTSUM) --junitfile $(ARTIFACTS)/junit.infra_docker.xml --raw-command cat $(ARTIFACTS)/junit.infra_docker.stdout exit $$(cat $(ARTIFACTS)/junit.infra_docker.exitcode) - cd $(CAPD_DIR); set +o errexit; (KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test -run "^TestFuzzyConversion$$" -json ./... $(TEST_ARGS); echo $$? > $(ARTIFACTS)/junit-fuzz.infra_docker.exitcode) | tee $(ARTIFACTS)/junit-fuzz.infra_docker.stdout + cd test/infrastructure; set +o errexit; (KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test -run "^TestFuzzyConversion$$" -json ./... $(TEST_ARGS); echo $$? > $(ARTIFACTS)/junit-fuzz.infra_docker.exitcode) | tee $(ARTIFACTS)/junit-fuzz.infra_docker.stdout $(GOTESTSUM) --junitfile $(ARTIFACTS)/junit-fuzz.infra_docker.xml --raw-command cat $(ARTIFACTS)/junit-fuzz.infra_docker.stdout exit $$(cat $(ARTIFACTS)/junit-fuzz.infra_docker.exitcode) + .PHONY: test-test-extension test-test-extension: $(SETUP_ENVTEST) ## Run unit and integration tests for the test extension cd $(TEST_EXTENSION_DIR); KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test -race ./... $(TEST_ARGS) @@ -1443,6 +1447,9 @@ $(GOLANGCI_LINT_BIN): $(GOLANGCI_LINT) ## Build a local copy of golangci-lint. .PHONY: $(GOVULNCHECK_BIN) $(GOVULNCHECK_BIN): $(GOVULNCHECK) ## Build a local copy of govulncheck. +.PHONY: $(CRANE_BIN) +$(CRANE_BIN): $(CRANE) ## Build a local copy of crane. + .PHONY: $(IMPORT_BOSS_BIN) $(IMPORT_BOSS_BIN): $(IMPORT_BOSS) @@ -1460,7 +1467,7 @@ $(CONVERSION_VERIFIER): $(TOOLS_DIR)/go.mod # Build conversion-verifier from too .PHONY: $(OPENAPI_GEN) $(OPENAPI_GEN): # Build openapi-gen from tools folder. - GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(OPENAPI_GEN_PKG) $(OPENAPI_GEN_BIN) $(OPENAPI_GEN_VER) + GOTOOLCHAIN=go1.24.13 GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(OPENAPI_GEN_PKG) $(OPENAPI_GEN_BIN) $(OPENAPI_GEN_VER) ## We are forcing a rebuilt of runtime-openapi-gen via PHONY so that we're always using an up-to-date version. .PHONY: $(RUNTIME_OPENAPI_GEN) @@ -1501,12 +1508,38 @@ $(GOLANGCI_LINT): # Build golangci-lint from tools folder. $(GOLANGCI_LINT_KAL): $(GOLANGCI_LINT) # Build golangci-lint-kal from custom configuration. cd $(TOOLS_DIR); $(GOLANGCI_LINT) custom -$(GOVULNCHECK): # Build govulncheck. - GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(GOVULNCHECK_PKG) $(GOVULNCHECK_BIN) $(GOVULNCHECK_VER) +$(CRANE): # Build crane. + GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(CRANE_PKG) $(CRANE_BIN) $(CRANE_VER) $(IMPORT_BOSS): # Build import-boss GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(IMPORT_BOSS_PKG) $(IMPORT_BOSS_BIN) $(IMPORT_BOSS_VER) +## -------------------------------------- +## govulncheck +## -------------------------------------- + +$(GOVULNCHECK): # Build govulncheck. + @if [ -z "${GOVULNCHECK_VER}" ]; then echo "GOVULNCHECK_VER is not set"; exit 1; fi + @if [ -d "$(GOVULNCHECK_TMP_DIR)" ]; then \ + echo "$(GOVULNCHECK_TMP_DIR) exists, skipping clone"; \ + else \ + git clone "https://github.com/golang/vuln.git" "$(GOVULNCHECK_TMP_DIR)"; \ + cd "$(GOVULNCHECK_TMP_DIR)"; \ + git checkout "$(GOVULNCHECK_VER)"; \ + git apply "$(ROOT_DIR)/$(GOVULNCHECK_DIR)/govulncheck.patch"; \ + fi + @cd "$(ROOT_DIR)/$(GOVULNCHECK_TMP_DIR)"; \ + if [ "$$(git describe --tag 2> /dev/null)" != "$(GOVULNCHECK_VER)" ]; then \ + echo "ERROR: checked out version $$(git describe --tag 2> /dev/null) does not match expected version $(GOVULNCHECK_VER)"; \ + exit 1; \ + fi + go build -C $(GOVULNCHECK_TMP_DIR) -o $(TOOLS_BIN_DIR)/$(GOVULNCHECK_BIN) ./cmd/govulncheck + +.PHONY: clean-govulncheck +clean-govulncheck: + rm -fr "$(GOVULNCHECK_TMP_DIR)" + + ## -------------------------------------- ## triage-party ## -------------------------------------- diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/OWNERS_ALIASES b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/OWNERS_ALIASES index 27c242bab1..36c7741035 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/OWNERS_ALIASES +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/OWNERS_ALIASES @@ -70,6 +70,13 @@ aliases: cluster-api-clusterctl-reviewers: - Jont828 + # ----------------------------------------------------------- + # OWNER_ALIASES for internal/controllers/machinepool + # ----------------------------------------------------------- + + cluster-api-machinepool-maintainers: + cluster-api-machinepool-reviewers: + # ----------------------------------------------------------- # OWNER_ALIASES for test # ----------------------------------------------------------- @@ -114,14 +121,17 @@ aliases: # ----------------------------------------------------------- cluster-api-release-lead: - - cprivitere + #- pratik-mahalle cluster-api-release-team: - - adilGhaffarDev + - arshadd-b + - aman4433 + - anshuman-agarwala + #- avaneesh634 + #- blind3dd - chandankumar4 - - chiukapoor - - cprivitere - - hackeramitkumar - mboersma - - shecodesmagic - - tsuzu + # - neoaggelos + - Peppi-Lotta + - SD-13 + - wendy-ha18 diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/Tiltfile b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/Tiltfile index f9d62e2be0..2a7628a13b 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/Tiltfile +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/Tiltfile @@ -2,7 +2,7 @@ clusterctl_cmd = "./bin/clusterctl" kubectl_cmd = "kubectl" -kubernetes_version = "v1.34.0" +kubernetes_version = "v1.35.0" load("ext://uibutton", "cmd_button", "location", "text_input") @@ -172,9 +172,9 @@ def load_provider_tilt_files(): tilt_helper_dockerfile_header = """ # Tilt image -FROM golang:1.24.9 as tilt-helper +FROM golang:1.25.10 as tilt-helper # Install delve. Note this should be kept in step with the Go release minor version. -RUN go install github.com/go-delve/delve/cmd/dlv@v1.24 +RUN go install github.com/go-delve/delve/cmd/dlv@v1.25 # Support live reloading with Tilt RUN wget --output-document /restart.sh --quiet https://raw.githubusercontent.com/tilt-dev/rerun-process-wrapper/master/restart.sh && \ wget --output-document /start.sh --quiet https://raw.githubusercontent.com/tilt-dev/rerun-process-wrapper/master/start.sh && \ @@ -183,7 +183,7 @@ RUN wget --output-document /restart.sh --quiet https://raw.githubusercontent.com """ tilt_dockerfile_header = """ -FROM golang:1.24.9 as tilt +FROM golang:1.25.10 as tilt WORKDIR / COPY --from=tilt-helper /process.txt . COPY --from=tilt-helper /start.sh . @@ -249,7 +249,7 @@ def build_go_binary(context, reload_deps, debug, go_main, binary_name, label): build_cmd = build_cmd, ), deps = live_reload_deps, - labels = [label, "ALL.binaries"], + labels = [label], ) def build_docker_image(image, context, binary_name, additional_docker_build_commands, additional_docker_helper_commands, port_forwards): @@ -290,7 +290,12 @@ def build_docker_image(image, context, binary_name, additional_docker_build_comm ref = image, context = context + "/.tiltbuild/bin/", dockerfile_contents = dockerfile_contents, - build_args = {"binary_name": binary_name}, + build_args = { + "binary_name": binary_name, + "http_proxy": os.getenv("http_proxy", ""), + "https_proxy": os.getenv("https_proxy", ""), + "no_proxy": os.getenv("no_proxy", ""), + }, target = "tilt", only = binary_name, live_update = [ @@ -363,7 +368,7 @@ def enable_provider(name, debug): workload = find_object_name(objs, "Deployment"), objects = [find_object_qualified_name(objs, "Provider")] + find_all_objects_names(additional_objs), new_name = label.lower() + "_controller", - labels = [label, "ALL.controllers"], + labels = [label], port_forwards = port_forwards, links = links, resource_deps = ["provider_crd"] + p.get("resource_deps", []), @@ -420,9 +425,9 @@ def deploy_provider_crds(): ) def deploy_observability(): - if "promtail" in settings.get("deploy_observability", []): - k8s_yaml(read_file("./.tiltbuild/yaml/promtail.observability.yaml"), allow_duplicates = True) - k8s_resource(workload = "promtail", extra_pod_selectors = [{"app": "promtail"}], labels = ["observability"], resource_deps = ["loki"], objects = ["promtail:serviceaccount"]) + if "alloy" in settings.get("deploy_observability", []): + k8s_yaml(read_file("./.tiltbuild/yaml/alloy.observability.yaml"), allow_duplicates = True) + k8s_resource(workload = "alloy", extra_pod_selectors = [{"app": "alloy"}], labels = ["observability"], resource_deps = ["loki"], objects = ["alloy:serviceaccount"]) if "loki" in settings.get("deploy_observability", []): k8s_yaml(read_file("./.tiltbuild/yaml/loki.observability.yaml"), allow_duplicates = True) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/.import-restrictions b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/.import-restrictions new file mode 100644 index 0000000000..f6f10b3ff5 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/.import-restrictions @@ -0,0 +1,5 @@ +rules: + - selectorRegexp: sigs[.]k8s[.]io/controller-runtime + allowedPrefixes: + - "sigs.k8s.io/controller-runtime/pkg/conversion" + forbiddenPrefixes: [] diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/condition_consts.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/condition_consts.go new file mode 100644 index 0000000000..e024d009c2 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/condition_consts.go @@ -0,0 +1,63 @@ +/* +Copyright 2021 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 v1beta1 + +import ( + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" +) + +// Conditions and condition Reasons for the KubeadmConfig object. + +const ( + // DataSecretAvailableCondition documents the status of the bootstrap secret generation process. + // + // NOTE: When the DataSecret generation starts the process completes immediately and within the + // same reconciliation, so the user will always see a transition from Wait to Generated without having + // evidence that BootstrapSecret generation is started/in progress. + DataSecretAvailableCondition clusterv1beta1.ConditionType = "DataSecretAvailable" + + // WaitingForClusterInfrastructureReason (Severity=Info) document a bootstrap secret generation process + // waiting for the cluster infrastructure to be ready. + // + // NOTE: Having the cluster infrastructure ready is a pre-condition for starting to create machines; + // the KubeadmConfig controller ensure this pre-condition is satisfied. + WaitingForClusterInfrastructureReason = "WaitingForClusterInfrastructure" + + // DataSecretGenerationFailedReason (Severity=Warning) documents a KubeadmConfig controller detecting + // an error while generating a data secret; those kind of errors are usually due to misconfigurations + // and user intervention is required to get them fixed. + DataSecretGenerationFailedReason = "DataSecretGenerationFailed" +) + +const ( + // CertificatesAvailableCondition documents that cluster certificates are available. + // + // NOTE: Cluster certificates are generated only for the KubeadmConfig object linked to the initial control plane + // machine, if the cluster is not using a control plane ref object, if the certificates are not provided + // by the users. + // IMPORTANT: This condition won't be re-created after clusterctl move. + CertificatesAvailableCondition clusterv1beta1.ConditionType = "CertificatesAvailable" + + // CertificatesGenerationFailedReason (Severity=Warning) documents a KubeadmConfig controller detecting + // an error while generating certificates; those kind of errors are usually temporary and the controller + // automatically recover from them. + CertificatesGenerationFailedReason = "CertificatesGenerationFailed" + + // CertificatesCorruptedReason (Severity=Error) documents a KubeadmConfig controller detecting + // an error while retrieving certificates for a joining node. + CertificatesCorruptedReason = "CertificatesCorrupted" +) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/conversion.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/conversion.go new file mode 100644 index 0000000000..8c5d7c57f4 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/conversion.go @@ -0,0 +1,998 @@ +/* +Copyright 2021 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 v1beta1 + +import ( + "fmt" + "reflect" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apimachineryconversion "k8s.io/apimachinery/pkg/conversion" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/conversion" + + bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" +) + +func (src *KubeadmConfig) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*bootstrapv1.KubeadmConfig) + if err := Convert_v1beta1_KubeadmConfig_To_v1beta2_KubeadmConfig(src, dst, nil); err != nil { + return err + } + + // Manually restore data. + restored := &bootstrapv1.KubeadmConfig{} + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { + return err + } + + // Recover intent for bool values converted to *bool. + initialization := bootstrapv1.KubeadmConfigInitializationStatus{} + restoredBootstrapDataSecretCreated := restored.Status.Initialization.DataSecretCreated + clusterv1.Convert_bool_To_Pointer_bool(src.Status.Ready, ok, restoredBootstrapDataSecretCreated, &initialization.DataSecretCreated) + if !reflect.DeepEqual(initialization, bootstrapv1.KubeadmConfigInitializationStatus{}) { + dst.Status.Initialization = initialization + } + if err := RestoreBoolIntentKubeadmConfigSpec(&src.Spec, &dst.Spec, ok, &restored.Spec); err != nil { + return err + } + + // Recover other values + if ok { + RestoreKubeadmConfigSpec(&restored.Spec, &dst.Spec) + } + + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.ConvertTo(&dst.Spec) + return nil +} + +func RestoreKubeadmConfigSpec(restored *bootstrapv1.KubeadmConfigSpec, dst *bootstrapv1.KubeadmConfigSpec) { + // Restore fields added in v1beta2 + if restored.InitConfiguration.IsDefined() && !reflect.DeepEqual(restored.InitConfiguration.Timeouts, bootstrapv1.Timeouts{}) { + dst.InitConfiguration.Timeouts = restored.InitConfiguration.Timeouts + } + if restored.JoinConfiguration.IsDefined() && !reflect.DeepEqual(restored.JoinConfiguration.Timeouts, bootstrapv1.Timeouts{}) { + dst.JoinConfiguration.Timeouts = restored.JoinConfiguration.Timeouts + } + if restored.ClusterConfiguration.CertificateValidityPeriodDays != 0 || restored.ClusterConfiguration.CACertificateValidityPeriodDays != 0 { + if restored.ClusterConfiguration.CertificateValidityPeriodDays != 0 { + dst.ClusterConfiguration.CertificateValidityPeriodDays = restored.ClusterConfiguration.CertificateValidityPeriodDays + } + if restored.ClusterConfiguration.CACertificateValidityPeriodDays != 0 { + dst.ClusterConfiguration.CACertificateValidityPeriodDays = restored.ClusterConfiguration.CACertificateValidityPeriodDays + } + } + if restored.ClusterConfiguration.EncryptionAlgorithm != "" { + dst.ClusterConfiguration.EncryptionAlgorithm = restored.ClusterConfiguration.EncryptionAlgorithm + } +} + +func RestoreBoolIntentKubeadmConfigSpec(src *KubeadmConfigSpec, dst *bootstrapv1.KubeadmConfigSpec, hasRestored bool, restored *bootstrapv1.KubeadmConfigSpec) error { + if dst.JoinConfiguration.Discovery.BootstrapToken.IsDefined() { + restoredUnsafeSkipCAVerification := restored.JoinConfiguration.Discovery.BootstrapToken.UnsafeSkipCAVerification + clusterv1.Convert_bool_To_Pointer_bool(src.JoinConfiguration.Discovery.BootstrapToken.UnsafeSkipCAVerification, hasRestored, restoredUnsafeSkipCAVerification, &dst.JoinConfiguration.Discovery.BootstrapToken.UnsafeSkipCAVerification) + } + + if dst.JoinConfiguration.Discovery.File.IsDefined() && dst.JoinConfiguration.Discovery.File.KubeConfig.IsDefined() { + if dst.JoinConfiguration.Discovery.File.KubeConfig.Cluster.IsDefined() { + restoredInsecureSkipTLSVerify := restored.JoinConfiguration.Discovery.File.KubeConfig.Cluster.InsecureSkipTLSVerify + clusterv1.Convert_bool_To_Pointer_bool(src.JoinConfiguration.Discovery.File.KubeConfig.Cluster.InsecureSkipTLSVerify, hasRestored, restoredInsecureSkipTLSVerify, &dst.JoinConfiguration.Discovery.File.KubeConfig.Cluster.InsecureSkipTLSVerify) + } + if dst.JoinConfiguration.Discovery.File.KubeConfig.User.Exec.IsDefined() { + restoredExecProvideClusterInfo := restored.JoinConfiguration.Discovery.File.KubeConfig.User.Exec.ProvideClusterInfo + clusterv1.Convert_bool_To_Pointer_bool(src.JoinConfiguration.Discovery.File.KubeConfig.User.Exec.ProvideClusterInfo, hasRestored, restoredExecProvideClusterInfo, &dst.JoinConfiguration.Discovery.File.KubeConfig.User.Exec.ProvideClusterInfo) + } + } + + for i, volume := range dst.ClusterConfiguration.APIServer.ExtraVolumes { + var srcVolume *HostPathMount + if src.ClusterConfiguration != nil { + for _, v := range src.ClusterConfiguration.APIServer.ExtraVolumes { + if v.HostPath == volume.HostPath { + srcVolume = &v + break + } + } + } + if srcVolume == nil { + return fmt.Errorf("apiServer extraVolume with hostPath %q not found in source data", volume.HostPath) + } + var restoredVolumeReadOnly *bool + for _, v := range restored.ClusterConfiguration.APIServer.ExtraVolumes { + if v.HostPath == volume.HostPath { + restoredVolumeReadOnly = v.ReadOnly + break + } + } + clusterv1.Convert_bool_To_Pointer_bool(srcVolume.ReadOnly, hasRestored, restoredVolumeReadOnly, &volume.ReadOnly) + dst.ClusterConfiguration.APIServer.ExtraVolumes[i] = volume + } + for i, volume := range dst.ClusterConfiguration.ControllerManager.ExtraVolumes { + var srcVolume *HostPathMount + if src.ClusterConfiguration != nil { + for _, v := range src.ClusterConfiguration.ControllerManager.ExtraVolumes { + if v.HostPath == volume.HostPath { + srcVolume = &v + break + } + } + } + if srcVolume == nil { + return fmt.Errorf("controllerManager extraVolume with hostPath %q not found in source data", volume.HostPath) + } + var restoredVolumeReadOnly *bool + for _, v := range restored.ClusterConfiguration.ControllerManager.ExtraVolumes { + if v.HostPath == volume.HostPath { + restoredVolumeReadOnly = v.ReadOnly + break + } + } + clusterv1.Convert_bool_To_Pointer_bool(srcVolume.ReadOnly, hasRestored, restoredVolumeReadOnly, &volume.ReadOnly) + dst.ClusterConfiguration.ControllerManager.ExtraVolumes[i] = volume + } + for i, volume := range dst.ClusterConfiguration.Scheduler.ExtraVolumes { + var srcVolume *HostPathMount + if src.ClusterConfiguration != nil { + for _, v := range src.ClusterConfiguration.Scheduler.ExtraVolumes { + if v.HostPath == volume.HostPath { + srcVolume = &v + break + } + } + } + if srcVolume == nil { + return fmt.Errorf("scheduler extraVolume with hostPath %q not found in source data", volume.HostPath) + } + var restoredVolumeReadOnly *bool + for _, v := range restored.ClusterConfiguration.Scheduler.ExtraVolumes { + if v.HostPath == volume.HostPath { + restoredVolumeReadOnly = v.ReadOnly + break + } + } + clusterv1.Convert_bool_To_Pointer_bool(srcVolume.ReadOnly, hasRestored, restoredVolumeReadOnly, &volume.ReadOnly) + dst.ClusterConfiguration.Scheduler.ExtraVolumes[i] = volume + } + + for i, file := range dst.Files { + var srcFile *File + for _, f := range src.Files { + if f.Path == file.Path { + srcFile = &f + break + } + } + if srcFile == nil { + return fmt.Errorf("file with path %q not found in source data", file.Path) + } + var restoredFileAppend *bool + for _, f := range restored.Files { + if f.Path == file.Path { + restoredFileAppend = f.Append + break + } + } + clusterv1.Convert_bool_To_Pointer_bool(srcFile.Append, hasRestored, restoredFileAppend, &file.Append) + dst.Files[i] = file + } + + if dst.Ignition.IsDefined() && dst.Ignition.ContainerLinuxConfig.IsDefined() { + restoredIgnitionStrict := restored.Ignition.ContainerLinuxConfig.Strict + clusterv1.Convert_bool_To_Pointer_bool(src.Ignition.ContainerLinuxConfig.Strict, hasRestored, restoredIgnitionStrict, &dst.Ignition.ContainerLinuxConfig.Strict) + } + return nil +} + +func (src *KubeadmConfigSpec) ConvertTo(dst *bootstrapv1.KubeadmConfigSpec) { + // Override with timeouts values already existing in v1beta1. + var initControlPlaneComponentHealthCheckSeconds *int32 + if src.ClusterConfiguration != nil && src.ClusterConfiguration.APIServer.TimeoutForControlPlane != nil { + dst.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = clusterv1.ConvertToSeconds(src.ClusterConfiguration.APIServer.TimeoutForControlPlane) + initControlPlaneComponentHealthCheckSeconds = dst.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds + } + if (src.JoinConfiguration != nil && src.JoinConfiguration.Discovery.Timeout != nil) || initControlPlaneComponentHealthCheckSeconds != nil { + dst.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = initControlPlaneComponentHealthCheckSeconds + if src.JoinConfiguration != nil && src.JoinConfiguration.Discovery.Timeout != nil { + dst.JoinConfiguration.Timeouts.TLSBootstrapSeconds = clusterv1.ConvertToSeconds(src.JoinConfiguration.Discovery.Timeout) + } + } +} + +func (dst *KubeadmConfig) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*bootstrapv1.KubeadmConfig) + if err := Convert_v1beta2_KubeadmConfig_To_v1beta1_KubeadmConfig(src, dst, nil); err != nil { + return err + } + + // Convert timeouts moved from one struct to another. + dst.Spec.ConvertFrom(&src.Spec) + + dropEmptyStringsKubeadmConfigSpec(&dst.Spec) + dropEmptyStringsKubeadmConfigStatus(&dst.Status) + + // Preserve Hub data on down-conversion except for metadata. + return utilconversion.MarshalData(src, dst) +} + +func (dst *KubeadmConfigSpec) ConvertFrom(src *bootstrapv1.KubeadmConfigSpec) { + // Convert timeouts moved from one struct to another. + if src.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds != nil { + if dst.ClusterConfiguration == nil { + dst.ClusterConfiguration = &ClusterConfiguration{} + } + dst.ClusterConfiguration.APIServer.TimeoutForControlPlane = clusterv1.ConvertFromSeconds(src.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds) + } + if reflect.DeepEqual(dst.InitConfiguration, &InitConfiguration{}) { + dst.InitConfiguration = nil + } + if src.JoinConfiguration.Timeouts.TLSBootstrapSeconds != nil { + if dst.JoinConfiguration == nil { + dst.JoinConfiguration = &JoinConfiguration{} + } + dst.JoinConfiguration.Discovery.Timeout = clusterv1.ConvertFromSeconds(src.JoinConfiguration.Timeouts.TLSBootstrapSeconds) + } + if reflect.DeepEqual(dst.JoinConfiguration, &JoinConfiguration{}) { + dst.JoinConfiguration = nil + } +} + +func (src *KubeadmConfigTemplate) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*bootstrapv1.KubeadmConfigTemplate) + if err := Convert_v1beta1_KubeadmConfigTemplate_To_v1beta2_KubeadmConfigTemplate(src, dst, nil); err != nil { + return err + } + + // Manually restore data. + restored := &bootstrapv1.KubeadmConfigTemplate{} + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { + return err + } + // Recover intent for bool values converted to *bool. + if err := RestoreBoolIntentKubeadmConfigSpec(&src.Spec.Template.Spec, &dst.Spec.Template.Spec, ok, &restored.Spec.Template.Spec); err != nil { + return err + } + + // Recover other values + if ok { + RestoreKubeadmConfigSpec(&restored.Spec.Template.Spec, &dst.Spec.Template.Spec) + } + + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.Template.Spec.ConvertTo(&dst.Spec.Template.Spec) + return nil +} + +func (dst *KubeadmConfigTemplate) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*bootstrapv1.KubeadmConfigTemplate) + if err := Convert_v1beta2_KubeadmConfigTemplate_To_v1beta1_KubeadmConfigTemplate(src, dst, nil); err != nil { + return err + } + + // Convert timeouts moved from one struct to another. + dst.Spec.Template.Spec.ConvertFrom(&src.Spec.Template.Spec) + + dropEmptyStringsKubeadmConfigSpec(&dst.Spec.Template.Spec) + + // Preserve Hub data on down-conversion except for metadata. + return utilconversion.MarshalData(src, dst) +} + +func Convert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in *bootstrapv1.InitConfiguration, out *InitConfiguration, s apimachineryconversion.Scope) error { + // Timeouts requires conversion at an upper level + if err := autoConvert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in, out, s); err != nil { + return err + } + if !reflect.DeepEqual(in.Patches, bootstrapv1.Patches{}) { + out.Patches = &Patches{} + if err := Convert_v1beta2_Patches_To_v1beta1_Patches(&in.Patches, out.Patches, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(in *bootstrapv1.JoinConfiguration, out *JoinConfiguration, s apimachineryconversion.Scope) error { + // Timeouts requires conversion at an upper level + if err := autoConvert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(in, out, s); err != nil { + return err + } + if !reflect.DeepEqual(in.Patches, bootstrapv1.Patches{}) { + out.Patches = &Patches{} + if err := Convert_v1beta2_Patches_To_v1beta1_Patches(&in.Patches, out.Patches, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta2_KubeadmConfigStatus_To_v1beta1_KubeadmConfigStatus(in *bootstrapv1.KubeadmConfigStatus, out *KubeadmConfigStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_KubeadmConfigStatus_To_v1beta1_KubeadmConfigStatus(in, out, s); err != nil { + return err + } + + // Reset conditions from autogenerated conversions + // NOTE: v1beta2 conditions should not be automatically be converted into legacy conditions (v1beta1). + out.Conditions = nil + + // Retrieve legacy conditions (v1beta1), failureReason and failureMessage from the deprecated field. + if in.Deprecated != nil && in.Deprecated.V1Beta1 != nil { + if in.Deprecated.V1Beta1.Conditions != nil { + clusterv1beta1.Convert_v1beta2_Deprecated_V1Beta1_Conditions_To_v1beta1_Conditions(&in.Deprecated.V1Beta1.Conditions, &out.Conditions) + } + out.FailureReason = in.Deprecated.V1Beta1.FailureReason + out.FailureMessage = in.Deprecated.V1Beta1.FailureMessage + } + + // Move initialization to old fields + out.Ready = ptr.Deref(in.Initialization.DataSecretCreated, false) + + // Move new conditions (v1beta2) to the v1beta2 field. + if in.Conditions == nil { + return nil + } + out.V1Beta2 = &KubeadmConfigV1Beta2Status{} + out.V1Beta2.Conditions = in.Conditions + return nil +} + +func Convert_v1beta2_APIServer_To_v1beta1_APIServer(in *bootstrapv1.APIServer, out *APIServer, s apimachineryconversion.Scope) error { + // Following fields require a custom conversions. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) + if in.ExtraEnvs == nil { + out.ExtraEnvs = nil + } else { + out.ExtraEnvs = make([]EnvVar, len(*in.ExtraEnvs)) + for i := range *in.ExtraEnvs { + if err := Convert_v1beta2_EnvVar_To_v1beta1_EnvVar(&(*in.ExtraEnvs)[i], &(out.ExtraEnvs)[i], s); err != nil { + return err + } + } + } + if err := convert_v1beta2_ExtraVolumes_To_v1beta1_ExtraVolumes(&in.ExtraVolumes, &out.ExtraVolumes, s); err != nil { + return err + } + return autoConvert_v1beta2_APIServer_To_v1beta1_APIServer(in, out, s) +} + +func Convert_v1beta2_ControllerManager_To_v1beta1_ControlPlaneComponent(in *bootstrapv1.ControllerManager, out *ControlPlaneComponent, s apimachineryconversion.Scope) error { + // Following fields require a custom conversions. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) + if in.ExtraEnvs == nil { + out.ExtraEnvs = nil + } else { + out.ExtraEnvs = make([]EnvVar, len(*in.ExtraEnvs)) + for i := range *in.ExtraEnvs { + if err := Convert_v1beta2_EnvVar_To_v1beta1_EnvVar(&(*in.ExtraEnvs)[i], &(out.ExtraEnvs)[i], s); err != nil { + return err + } + } + } + return convert_v1beta2_ExtraVolumes_To_v1beta1_ExtraVolumes(&in.ExtraVolumes, &out.ExtraVolumes, s) +} + +func Convert_v1beta2_Scheduler_To_v1beta1_ControlPlaneComponent(in *bootstrapv1.Scheduler, out *ControlPlaneComponent, s apimachineryconversion.Scope) error { + // Following fields require a custom conversions. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) + if in.ExtraEnvs == nil { + out.ExtraEnvs = nil + } else { + out.ExtraEnvs = make([]EnvVar, len(*in.ExtraEnvs)) + for i := range *in.ExtraEnvs { + if err := Convert_v1beta2_EnvVar_To_v1beta1_EnvVar(&(*in.ExtraEnvs)[i], &(out.ExtraEnvs)[i], s); err != nil { + return err + } + } + } + return convert_v1beta2_ExtraVolumes_To_v1beta1_ExtraVolumes(&in.ExtraVolumes, &out.ExtraVolumes, s) +} + +func convert_v1beta2_ExtraVolumes_To_v1beta1_ExtraVolumes(in *[]bootstrapv1.HostPathMount, out *[]HostPathMount, s apimachineryconversion.Scope) error { + if in != nil && len(*in) > 0 { + *out = make([]HostPathMount, len(*in)) + for i := range *in { + if err := Convert_v1beta2_HostPathMount_To_v1beta1_HostPathMount(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + *out = nil + } + return nil +} + +func Convert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(in *bootstrapv1.LocalEtcd, out *LocalEtcd, s apimachineryconversion.Scope) error { + // Following fields require a custom conversions. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) + if in.ExtraEnvs == nil { + out.ExtraEnvs = nil + } else { + out.ExtraEnvs = make([]EnvVar, len(*in.ExtraEnvs)) + for i := range *in.ExtraEnvs { + if err := Convert_v1beta2_EnvVar_To_v1beta1_EnvVar(&(*in.ExtraEnvs)[i], &(out.ExtraEnvs)[i], s); err != nil { + return err + } + } + } + out.ImageRepository = in.ImageRepository + out.ImageTag = in.ImageTag + return autoConvert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(in, out, s) +} + +func Convert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apimachineryconversion.Scope) error { + // Following fields require a custom conversions. + out.KubeletExtraArgs = bootstrapv1.ConvertFromArgs(in.KubeletExtraArgs) + if in.Taints == nil { + out.Taints = nil + } else { + out.Taints = *in.Taints + } + return autoConvert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in, out, s) +} + +func Convert_v1beta2_BootstrapToken_To_v1beta1_BootstrapToken(in *bootstrapv1.BootstrapToken, out *BootstrapToken, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_BootstrapToken_To_v1beta1_BootstrapToken(in, out, s); err != nil { + return err + } + out.TTL = clusterv1.ConvertFromSeconds(in.TTLSeconds) + if !reflect.DeepEqual(in.Expires, metav1.Time{}) { + out.Expires = ptr.To(in.Expires) + } + if !reflect.DeepEqual(in.Token, bootstrapv1.BootstrapTokenString{}) { + out.Token = &BootstrapTokenString{} + if err := autoConvert_v1beta2_BootstrapTokenString_To_v1beta1_BootstrapTokenString(&in.Token, out.Token, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_APIServer_To_v1beta2_APIServer(in *APIServer, out *bootstrapv1.APIServer, s apimachineryconversion.Scope) error { + // TimeoutForControlPlane has been removed in v1beta2 + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + if in.ExtraEnvs == nil { + out.ExtraEnvs = nil + } else { + out.ExtraEnvs = ptr.To(make([]bootstrapv1.EnvVar, len(in.ExtraEnvs))) + for i := range in.ExtraEnvs { + if err := Convert_v1beta1_EnvVar_To_v1beta2_EnvVar(&(in.ExtraEnvs)[i], &(*out.ExtraEnvs)[i], s); err != nil { + return err + } + } + } + if err := convert_v1beta1_ExtraVolumes_To_v1beta2_ExtraVolumes(&in.ExtraVolumes, &out.ExtraVolumes, s); err != nil { + return err + } + return autoConvert_v1beta1_APIServer_To_v1beta2_APIServer(in, out, s) +} + +func Convert_v1beta1_ControlPlaneComponent_To_v1beta2_ControllerManager(in *ControlPlaneComponent, out *bootstrapv1.ControllerManager, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + if in.ExtraEnvs == nil { + out.ExtraEnvs = nil + } else { + out.ExtraEnvs = ptr.To(make([]bootstrapv1.EnvVar, len(in.ExtraEnvs))) + for i := range in.ExtraEnvs { + if err := Convert_v1beta1_EnvVar_To_v1beta2_EnvVar(&(in.ExtraEnvs)[i], &(*out.ExtraEnvs)[i], s); err != nil { + return err + } + } + } + return convert_v1beta1_ExtraVolumes_To_v1beta2_ExtraVolumes(&in.ExtraVolumes, &out.ExtraVolumes, s) +} + +func Convert_v1beta1_ControlPlaneComponent_To_v1beta2_Scheduler(in *ControlPlaneComponent, out *bootstrapv1.Scheduler, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + if in.ExtraEnvs == nil { + out.ExtraEnvs = nil + } else { + out.ExtraEnvs = ptr.To(make([]bootstrapv1.EnvVar, len(in.ExtraEnvs))) + for i := range in.ExtraEnvs { + if err := Convert_v1beta1_EnvVar_To_v1beta2_EnvVar(&(in.ExtraEnvs)[i], &(*out.ExtraEnvs)[i], s); err != nil { + return err + } + } + } + return convert_v1beta1_ExtraVolumes_To_v1beta2_ExtraVolumes(&in.ExtraVolumes, &out.ExtraVolumes, s) +} + +func convert_v1beta1_ExtraVolumes_To_v1beta2_ExtraVolumes(in *[]HostPathMount, out *[]bootstrapv1.HostPathMount, s apimachineryconversion.Scope) error { + if in != nil && len(*in) > 0 { + *out = make([]bootstrapv1.HostPathMount, len(*in)) + for i := range *in { + if err := Convert_v1beta1_HostPathMount_To_v1beta2_HostPathMount(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + *out = nil + } + return nil +} + +func Convert_v1beta1_Discovery_To_v1beta2_Discovery(in *Discovery, out *bootstrapv1.Discovery, s apimachineryconversion.Scope) error { + // Timeout has been removed in v1beta2 + if err := autoConvert_v1beta1_Discovery_To_v1beta2_Discovery(in, out, s); err != nil { + return err + } + if in.BootstrapToken != nil { + if err := Convert_v1beta1_BootstrapTokenDiscovery_To_v1beta2_BootstrapTokenDiscovery(in.BootstrapToken, &out.BootstrapToken, s); err != nil { + return err + } + } + if in.File != nil { + if err := Convert_v1beta1_FileDiscovery_To_v1beta2_FileDiscovery(in.File, &out.File, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_ClusterConfiguration_To_v1beta2_ClusterConfiguration(in *ClusterConfiguration, out *bootstrapv1.ClusterConfiguration, s apimachineryconversion.Scope) error { + return autoConvert_v1beta1_ClusterConfiguration_To_v1beta2_ClusterConfiguration(in, out, s) +} + +func Convert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *bootstrapv1.LocalEtcd, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + if in.ExtraEnvs == nil { + out.ExtraEnvs = nil + } else { + out.ExtraEnvs = ptr.To(make([]bootstrapv1.EnvVar, len(in.ExtraEnvs))) + for i := range in.ExtraEnvs { + if err := Convert_v1beta1_EnvVar_To_v1beta2_EnvVar(&(in.ExtraEnvs)[i], &(*out.ExtraEnvs)[i], s); err != nil { + return err + } + } + } + out.ImageRepository = in.ImageRepository + out.ImageTag = in.ImageTag + return autoConvert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) +} + +func Convert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *bootstrapv1.NodeRegistrationOptions, s apimachineryconversion.Scope) error { + out.KubeletExtraArgs = bootstrapv1.ConvertToArgs(in.KubeletExtraArgs) + if in.Taints == nil { + out.Taints = nil + } else { + out.Taints = ptr.To(in.Taints) + } + return autoConvert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) +} + +func Convert_v1beta1_KubeadmConfigStatus_To_v1beta2_KubeadmConfigStatus(in *KubeadmConfigStatus, out *bootstrapv1.KubeadmConfigStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_KubeadmConfigStatus_To_v1beta2_KubeadmConfigStatus(in, out, s); err != nil { + return err + } + + // Reset conditions from autogenerated conversions + // NOTE: v1beta1 conditions should not be automatically be converted into v1beta2 conditions. + out.Conditions = nil + + // Retrieve new conditions (v1beta2) from the v1beta2 field. + if in.V1Beta2 != nil { + out.Conditions = in.V1Beta2.Conditions + } + + // Move legacy conditions (v1beta1), failureReason and failureMessage to the deprecated field. + if out.Deprecated == nil { + out.Deprecated = &bootstrapv1.KubeadmConfigDeprecatedStatus{} + } + if out.Deprecated.V1Beta1 == nil { + out.Deprecated.V1Beta1 = &bootstrapv1.KubeadmConfigV1Beta1DeprecatedStatus{} + } + if in.Conditions != nil { + clusterv1beta1.Convert_v1beta1_Conditions_To_v1beta2_Deprecated_V1Beta1_Conditions(&in.Conditions, &out.Deprecated.V1Beta1.Conditions) + } + out.Deprecated.V1Beta1.FailureReason = in.FailureReason + out.Deprecated.V1Beta1.FailureMessage = in.FailureMessage + + // Move ready to Initialization is implemented in ConvertTo + return nil +} + +func Convert_v1beta1_BootstrapToken_To_v1beta2_BootstrapToken(in *BootstrapToken, out *bootstrapv1.BootstrapToken, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_BootstrapToken_To_v1beta2_BootstrapToken(in, out, s); err != nil { + return err + } + out.TTLSeconds = clusterv1.ConvertToSeconds(in.TTL) + if in.Expires != nil && !reflect.DeepEqual(in.Expires, &metav1.Time{}) { + out.Expires = *in.Expires + } + if in.Token != nil { + if err := autoConvert_v1beta1_BootstrapTokenString_To_v1beta2_BootstrapTokenString(in.Token, &out.Token, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_InitConfiguration_To_v1beta2_InitConfiguration(in *InitConfiguration, out *bootstrapv1.InitConfiguration, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_InitConfiguration_To_v1beta2_InitConfiguration(in, out, s); err != nil { + return err + } + if in.Patches != nil { + if err := Convert_v1beta1_Patches_To_v1beta2_Patches(in.Patches, &out.Patches, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(in *JoinConfiguration, out *bootstrapv1.JoinConfiguration, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(in, out, s); err != nil { + return err + } + if in.Patches != nil { + if err := Convert_v1beta1_Patches_To_v1beta2_Patches(in.Patches, &out.Patches, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_DNS_To_v1beta2_DNS(in *DNS, out *bootstrapv1.DNS, _ apimachineryconversion.Scope) error { + out.ImageRepository = in.ImageRepository + out.ImageTag = in.ImageTag + return nil +} + +func Convert_v1beta2_DNS_To_v1beta1_DNS(in *bootstrapv1.DNS, out *DNS, _ apimachineryconversion.Scope) error { + out.ImageRepository = in.ImageRepository + out.ImageTag = in.ImageTag + return nil +} + +func Convert_v1beta1_Etcd_To_v1beta2_Etcd(in *Etcd, out *bootstrapv1.Etcd, s apimachineryconversion.Scope) error { + if in.Local != nil { + if err := Convert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in.Local, &out.Local, s); err != nil { + return err + } + } + if in.External != nil { + if err := Convert_v1beta1_ExternalEtcd_To_v1beta2_ExternalEtcd(in.External, &out.External, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta2_Etcd_To_v1beta1_Etcd(in *bootstrapv1.Etcd, out *Etcd, s apimachineryconversion.Scope) error { + if in.Local.IsDefined() { + out.Local = &LocalEtcd{} + if err := Convert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(&in.Local, out.Local, s); err != nil { + return err + } + } + if in.External.IsDefined() { + out.External = &ExternalEtcd{} + if err := Convert_v1beta2_ExternalEtcd_To_v1beta1_ExternalEtcd(&in.External, out.External, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta2_Discovery_To_v1beta1_Discovery(in *bootstrapv1.Discovery, out *Discovery, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_Discovery_To_v1beta1_Discovery(in, out, s); err != nil { + return err + } + if !reflect.DeepEqual(in.BootstrapToken, bootstrapv1.BootstrapTokenDiscovery{}) { + out.BootstrapToken = &BootstrapTokenDiscovery{} + if err := Convert_v1beta2_BootstrapTokenDiscovery_To_v1beta1_BootstrapTokenDiscovery(&in.BootstrapToken, out.BootstrapToken, s); err != nil { + return err + } + } + if !reflect.DeepEqual(in.File, bootstrapv1.FileDiscovery{}) { + out.File = &FileDiscovery{} + if err := Convert_v1beta2_FileDiscovery_To_v1beta1_FileDiscovery(&in.File, out.File, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_FileDiscovery_To_v1beta2_FileDiscovery(in *FileDiscovery, out *bootstrapv1.FileDiscovery, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_FileDiscovery_To_v1beta2_FileDiscovery(in, out, s); err != nil { + return err + } + if in.KubeConfig != nil { + if err := Convert_v1beta1_FileDiscoveryKubeConfig_To_v1beta2_FileDiscoveryKubeConfig(in.KubeConfig, &out.KubeConfig, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_FileDiscoveryKubeConfig_To_v1beta2_FileDiscoveryKubeConfig(in *FileDiscoveryKubeConfig, out *bootstrapv1.FileDiscoveryKubeConfig, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_FileDiscoveryKubeConfig_To_v1beta2_FileDiscoveryKubeConfig(in, out, s); err != nil { + return err + } + if in.Cluster != nil { + if err := Convert_v1beta1_KubeConfigCluster_To_v1beta2_KubeConfigCluster(in.Cluster, &out.Cluster, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_KubeConfigUser_To_v1beta2_KubeConfigUser(in *KubeConfigUser, out *bootstrapv1.KubeConfigUser, s apimachineryconversion.Scope) error { + if in.AuthProvider != nil { + if err := Convert_v1beta1_KubeConfigAuthProvider_To_v1beta2_KubeConfigAuthProvider(in.AuthProvider, &out.AuthProvider, s); err != nil { + return err + } + } + if in.Exec != nil { + if err := Convert_v1beta1_KubeConfigAuthExec_To_v1beta2_KubeConfigAuthExec(in.Exec, &out.Exec, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta2_FileDiscovery_To_v1beta1_FileDiscovery(in *bootstrapv1.FileDiscovery, out *FileDiscovery, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_FileDiscovery_To_v1beta1_FileDiscovery(in, out, s); err != nil { + return err + } + if !reflect.DeepEqual(in.KubeConfig, bootstrapv1.FileDiscoveryKubeConfig{}) { + out.KubeConfig = &FileDiscoveryKubeConfig{} + if err := Convert_v1beta2_FileDiscoveryKubeConfig_To_v1beta1_FileDiscoveryKubeConfig(&in.KubeConfig, out.KubeConfig, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta2_FileDiscoveryKubeConfig_To_v1beta1_FileDiscoveryKubeConfig(in *bootstrapv1.FileDiscoveryKubeConfig, out *FileDiscoveryKubeConfig, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_FileDiscoveryKubeConfig_To_v1beta1_FileDiscoveryKubeConfig(in, out, s); err != nil { + return err + } + if !reflect.DeepEqual(in.Cluster, bootstrapv1.KubeConfigCluster{}) { + out.Cluster = &KubeConfigCluster{} + if err := Convert_v1beta2_KubeConfigCluster_To_v1beta1_KubeConfigCluster(&in.Cluster, out.Cluster, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta2_KubeConfigUser_To_v1beta1_KubeConfigUser(in *bootstrapv1.KubeConfigUser, out *KubeConfigUser, s apimachineryconversion.Scope) error { + if !reflect.DeepEqual(in.AuthProvider, bootstrapv1.KubeConfigAuthProvider{}) { + out.AuthProvider = &KubeConfigAuthProvider{} + if err := Convert_v1beta2_KubeConfigAuthProvider_To_v1beta1_KubeConfigAuthProvider(&in.AuthProvider, out.AuthProvider, s); err != nil { + return err + } + } + if !reflect.DeepEqual(in.Exec, bootstrapv1.KubeConfigAuthExec{}) { + out.Exec = &KubeConfigAuthExec{} + if err := Convert_v1beta2_KubeConfigAuthExec_To_v1beta1_KubeConfigAuthExec(&in.Exec, out.Exec, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(in *KubeadmConfigSpec, out *bootstrapv1.KubeadmConfigSpec, s apimachineryconversion.Scope) error { + // NOTE: v1beta2 KubeadmConfigSpec does not have UseExperimentalRetryJoin anymore, so it's fine to just lose this field. + if err := autoConvert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(in, out, s); err != nil { + return err + } + if in.ClusterConfiguration != nil { + if err := Convert_v1beta1_ClusterConfiguration_To_v1beta2_ClusterConfiguration(in.ClusterConfiguration, &out.ClusterConfiguration, s); err != nil { + return err + } + } + if in.InitConfiguration != nil { + if err := Convert_v1beta1_InitConfiguration_To_v1beta2_InitConfiguration(in.InitConfiguration, &out.InitConfiguration, s); err != nil { + return err + } + } + if in.JoinConfiguration != nil { + if err := Convert_v1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(in.JoinConfiguration, &out.JoinConfiguration, s); err != nil { + return err + } + } + if in.DiskSetup != nil { + if err := Convert_v1beta1_DiskSetup_To_v1beta2_DiskSetup(in.DiskSetup, &out.DiskSetup, s); err != nil { + return err + } + } + if in.NTP != nil { + if err := Convert_v1beta1_NTP_To_v1beta2_NTP(in.NTP, &out.NTP, s); err != nil { + return err + } + } + if in.Ignition != nil { + if err := Convert_v1beta1_IgnitionSpec_To_v1beta2_IgnitionSpec(in.Ignition, &out.Ignition, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta2_KubeadmConfigSpec_To_v1beta1_KubeadmConfigSpec(in *bootstrapv1.KubeadmConfigSpec, out *KubeadmConfigSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_KubeadmConfigSpec_To_v1beta1_KubeadmConfigSpec(in, out, s); err != nil { + return err + } + if !reflect.DeepEqual(in.ClusterConfiguration, bootstrapv1.ClusterConfiguration{}) { + out.ClusterConfiguration = &ClusterConfiguration{} + if err := Convert_v1beta2_ClusterConfiguration_To_v1beta1_ClusterConfiguration(&in.ClusterConfiguration, out.ClusterConfiguration, s); err != nil { + return err + } + } + if !reflect.DeepEqual(in.InitConfiguration, bootstrapv1.InitConfiguration{}) { + out.InitConfiguration = &InitConfiguration{} + if err := Convert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(&in.InitConfiguration, out.InitConfiguration, s); err != nil { + return err + } + } + if !reflect.DeepEqual(in.JoinConfiguration, bootstrapv1.JoinConfiguration{}) { + out.JoinConfiguration = &JoinConfiguration{} + if err := Convert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(&in.JoinConfiguration, out.JoinConfiguration, s); err != nil { + return err + } + } + if !reflect.DeepEqual(in.DiskSetup, bootstrapv1.DiskSetup{}) { + out.DiskSetup = &DiskSetup{} + if err := Convert_v1beta2_DiskSetup_To_v1beta1_DiskSetup(&in.DiskSetup, out.DiskSetup, s); err != nil { + return err + } + } + if !reflect.DeepEqual(in.NTP, bootstrapv1.NTP{}) { + out.NTP = &NTP{} + if err := Convert_v1beta2_NTP_To_v1beta1_NTP(&in.NTP, out.NTP, s); err != nil { + return err + } + } + if !reflect.DeepEqual(in.Ignition, bootstrapv1.IgnitionSpec{}) { + out.Ignition = &IgnitionSpec{} + if err := Convert_v1beta2_IgnitionSpec_To_v1beta1_IgnitionSpec(&in.Ignition, out.Ignition, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_IgnitionSpec_To_v1beta2_IgnitionSpec(in *IgnitionSpec, out *bootstrapv1.IgnitionSpec, s apimachineryconversion.Scope) error { + if in.ContainerLinuxConfig != nil { + if err := Convert_v1beta1_ContainerLinuxConfig_To_v1beta2_ContainerLinuxConfig(in.ContainerLinuxConfig, &out.ContainerLinuxConfig, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta2_IgnitionSpec_To_v1beta1_IgnitionSpec(in *bootstrapv1.IgnitionSpec, out *IgnitionSpec, s apimachineryconversion.Scope) error { + if !reflect.DeepEqual(in.ContainerLinuxConfig, bootstrapv1.ContainerLinuxConfig{}) { + out.ContainerLinuxConfig = &ContainerLinuxConfig{} + if err := Convert_v1beta2_ContainerLinuxConfig_To_v1beta1_ContainerLinuxConfig(&in.ContainerLinuxConfig, out.ContainerLinuxConfig, s); err != nil { + return err + } + } + return nil +} + +// Implement local conversion func because conversion-gen is not aware of conversion func in other packages (see https://github.com/kubernetes/code-generator/issues/94) + +func Convert_v1beta1_ObjectMeta_To_v1beta2_ObjectMeta(in *clusterv1beta1.ObjectMeta, out *clusterv1.ObjectMeta, s apimachineryconversion.Scope) error { + return clusterv1beta1.Convert_v1beta1_ObjectMeta_To_v1beta2_ObjectMeta(in, out, s) +} + +func Convert_v1beta2_ObjectMeta_To_v1beta1_ObjectMeta(in *clusterv1.ObjectMeta, out *clusterv1beta1.ObjectMeta, s apimachineryconversion.Scope) error { + return clusterv1beta1.Convert_v1beta2_ObjectMeta_To_v1beta1_ObjectMeta(in, out, s) +} + +func Convert_v1_Condition_To_v1beta1_Condition(in *metav1.Condition, out *clusterv1beta1.Condition, s apimachineryconversion.Scope) error { + return clusterv1beta1.Convert_v1_Condition_To_v1beta1_Condition(in, out, s) +} + +func Convert_v1beta1_Condition_To_v1_Condition(in *clusterv1beta1.Condition, out *metav1.Condition, s apimachineryconversion.Scope) error { + return clusterv1beta1.Convert_v1beta1_Condition_To_v1_Condition(in, out, s) +} + +func Convert_v1beta2_ClusterConfiguration_To_v1beta1_ClusterConfiguration(in *bootstrapv1.ClusterConfiguration, out *ClusterConfiguration, s apimachineryconversion.Scope) error { + return autoConvert_v1beta2_ClusterConfiguration_To_v1beta1_ClusterConfiguration(in, out, s) +} + +func Convert_v1beta1_User_To_v1beta2_User(in *User, out *bootstrapv1.User, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_User_To_v1beta2_User(in, out, s); err != nil { + return err + } + if in.PasswdFrom != nil { + if err := Convert_v1beta1_PasswdSource_To_v1beta2_PasswdSource(in.PasswdFrom, &out.PasswdFrom, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta2_User_To_v1beta1_User(in *bootstrapv1.User, out *User, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_User_To_v1beta1_User(in, out, s); err != nil { + return err + } + if in.PasswdFrom.IsDefined() { + out.PasswdFrom = &PasswdSource{} + if err := Convert_v1beta2_PasswdSource_To_v1beta1_PasswdSource(&in.PasswdFrom, out.PasswdFrom, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_File_To_v1beta2_File(in *File, out *bootstrapv1.File, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_File_To_v1beta2_File(in, out, s); err != nil { + return err + } + if in.ContentFrom != nil { + if err := Convert_v1beta1_FileSource_To_v1beta2_FileSource(in.ContentFrom, &out.ContentFrom, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta2_File_To_v1beta1_File(in *bootstrapv1.File, out *File, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_File_To_v1beta1_File(in, out, s); err != nil { + return err + } + if in.ContentFrom.IsDefined() { + out.ContentFrom = &FileSource{} + if err := Convert_v1beta2_FileSource_To_v1beta1_FileSource(&in.ContentFrom, out.ContentFrom, s); err != nil { + return err + } + } + return nil +} + +func dropEmptyStringsKubeadmConfigSpec(dst *KubeadmConfigSpec) { + for i, u := range dst.Users { + dropEmptyString(&u.Gecos) + dropEmptyString(&u.Groups) + dropEmptyString(&u.HomeDir) + dropEmptyString(&u.Shell) + dropEmptyString(&u.Passwd) + dropEmptyString(&u.PrimaryGroup) + dropEmptyString(&u.Sudo) + dst.Users[i] = u + } + + if dst.DiskSetup != nil { + for i, p := range dst.DiskSetup.Partitions { + dropEmptyString(&p.TableType) + dst.DiskSetup.Partitions[i] = p + } + for i, f := range dst.DiskSetup.Filesystems { + dropEmptyString(&f.Partition) + dropEmptyString(&f.ReplaceFS) + dst.DiskSetup.Filesystems[i] = f + } + } +} + +func dropEmptyStringsKubeadmConfigStatus(dst *KubeadmConfigStatus) { + dropEmptyString(&dst.DataSecretName) +} + +func dropEmptyString(s **string) { + if *s != nil && **s == "" { + *s = nil + } +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/doc.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/doc.go similarity index 60% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/doc.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/doc.go index a58a55ea32..58bfea72fc 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/doc.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/doc.go @@ -14,5 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package controllers implements experimental controllers. -package controllers +// Package v1beta1 contains API Schema definitions for the kubeadm v1beta1 API group. +// +k8s:conversion-gen=sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2 +// +kubebuilder:object:generate=true +// +groupName=bootstrap.cluster.x-k8s.io +// +// Deprecated: This package is deprecated and is going to be removed when support for v1beta1 will be dropped. +package v1beta1 diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/groupversion_info.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/groupversion_info.go new file mode 100644 index 0000000000..c85e040a1a --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/groupversion_info.go @@ -0,0 +1,45 @@ +/* +Copyright 2021 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 v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + // GroupVersion is group version used to register these objects. + GroupVersion = schema.GroupVersion{Group: "bootstrap.cluster.x-k8s.io", Version: "v1beta1"} + + // schemeBuilder is used to add go types to the GroupVersionKind scheme. + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = schemeBuilder.AddToScheme + + // localSchemeBuilder is used for type conversions. + localSchemeBuilder = &schemeBuilder + + objectTypes = []runtime.Object{} +) + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(GroupVersion, objectTypes...) + metav1.AddToGroupVersion(scheme, GroupVersion) + return nil +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/kubeadm_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/kubeadm_types.go new file mode 100644 index 0000000000..97f7dafca2 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/kubeadm_types.go @@ -0,0 +1,893 @@ +/* +Copyright 2021 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 v1beta1 + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + bootstrapapi "k8s.io/cluster-bootstrap/token/api" + bootstraputil "k8s.io/cluster-bootstrap/token/util" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// InitConfiguration contains a list of elements that is specific "kubeadm init"-only runtime +// information. +type InitConfiguration struct { + metav1.TypeMeta `json:",inline"` + + // bootstrapTokens is respected at `kubeadm init` time and describes a set of Bootstrap Tokens to create. + // This information IS NOT uploaded to the kubeadm cluster configmap, partly because of its sensitive nature + // +optional + // +kubebuilder:validation:MaxItems=100 + BootstrapTokens []BootstrapToken `json:"bootstrapTokens,omitempty"` + + // nodeRegistration holds fields that relate to registering the new control-plane node to the cluster. + // When used in the context of control plane nodes, NodeRegistration should remain consistent + // across both InitConfiguration and JoinConfiguration + // +optional + NodeRegistration NodeRegistrationOptions `json:"nodeRegistration,omitempty"` + + // localAPIEndpoint represents the endpoint of the API server instance that's deployed on this control plane node + // In HA setups, this differs from ClusterConfiguration.ControlPlaneEndpoint in the sense that ControlPlaneEndpoint + // is the global endpoint for the cluster, which then loadbalances the requests to each individual API server. This + // configuration object lets you customize what IP/DNS name and port the local API server advertises it's accessible + // on. By default, kubeadm tries to auto-detect the IP of the default interface and use that, but in case that process + // fails you may set the desired value here. + // +optional + LocalAPIEndpoint APIEndpoint `json:"localAPIEndpoint,omitempty"` + + // skipPhases is a list of phases to skip during command execution. + // The list of phases can be obtained with the "kubeadm init --help" command. + // This option takes effect only on Kubernetes >=1.22.0. + // +optional + // +kubebuilder:validation:MaxItems=50 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=256 + SkipPhases []string `json:"skipPhases,omitempty"` + + // patches contains options related to applying patches to components deployed by kubeadm during + // "kubeadm init". The minimum kubernetes version needed to support Patches is v1.22 + // +optional + Patches *Patches `json:"patches,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterConfiguration contains cluster-wide configuration for a kubeadm cluster. +type ClusterConfiguration struct { + metav1.TypeMeta `json:",inline"` + + // etcd holds configuration for etcd. + // NB: This value defaults to a Local (stacked) etcd + // +optional + Etcd Etcd `json:"etcd,omitempty"` + + // networking holds configuration for the networking topology of the cluster. + // NB: This value defaults to the Cluster object spec.clusterNetwork. + // +optional + Networking Networking `json:"networking,omitempty"` + + // kubernetesVersion is the target version of the control plane. + // NB: This value defaults to the Machine object spec.version + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + KubernetesVersion string `json:"kubernetesVersion,omitempty"` + + // controlPlaneEndpoint sets a stable IP address or DNS name for the control plane; it + // can be a valid IP address or a RFC-1123 DNS subdomain, both with optional TCP port. + // In case the ControlPlaneEndpoint is not specified, the AdvertiseAddress + BindPort + // are used; in case the ControlPlaneEndpoint is specified but without a TCP port, + // the BindPort is used. + // Possible usages are: + // e.g. In a cluster with more than one control plane instances, this field should be + // assigned the address of the external load balancer in front of the + // control plane instances. + // e.g. in environments with enforced node recycling, the ControlPlaneEndpoint + // could be used for assigning a stable DNS to the control plane. + // NB: This value defaults to the first value in the Cluster object status.apiEndpoints array. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + ControlPlaneEndpoint string `json:"controlPlaneEndpoint,omitempty"` + + // apiServer contains extra settings for the API server control plane component + // +optional + APIServer APIServer `json:"apiServer,omitempty"` + + // controllerManager contains extra settings for the controller manager control plane component + // +optional + ControllerManager ControlPlaneComponent `json:"controllerManager,omitempty"` + + // scheduler contains extra settings for the scheduler control plane component + // +optional + Scheduler ControlPlaneComponent `json:"scheduler,omitempty"` + + // dns defines the options for the DNS add-on installed in the cluster. + // +optional + DNS DNS `json:"dns,omitempty"` + + // certificatesDir specifies where to store or look for all required certificates. + // NB: if not provided, this will default to `/etc/kubernetes/pki` + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + CertificatesDir string `json:"certificatesDir,omitempty"` + + // imageRepository sets the container registry to pull images from. + // * If not set, the default registry of kubeadm will be used, i.e. + // * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0 + // * k8s.gcr.io (old registry): all older versions + // Please note that when imageRepository is not set we don't allow upgrades to + // versions >= v1.22.0 which use the old registry (k8s.gcr.io). Please use + // a newer patch version with the new registry instead (i.e. >= v1.22.17, + // >= v1.23.15, >= v1.24.9, >= v1.25.0). + // * If the version is a CI build (kubernetes version starts with `ci/` or `ci-cross/`) + // `gcr.io/k8s-staging-ci-images` will be used as a default for control plane components + // and for kube-proxy, while `registry.k8s.io` will be used for all the other images. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + ImageRepository string `json:"imageRepository,omitempty"` + + // featureGates enabled by the user. + // +optional + FeatureGates map[string]bool `json:"featureGates,omitempty"` + + // clusterName is the cluster name + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=63 + ClusterName string `json:"clusterName,omitempty"` +} + +// ControlPlaneComponent holds settings common to control plane component of the cluster. +type ControlPlaneComponent struct { + // extraArgs is an extra set of flags to pass to the control plane component. + // TODO: This is temporary and ideally we would like to switch all components to use ComponentConfig + ConfigMaps. + // +optional + ExtraArgs map[string]string `json:"extraArgs,omitempty"` + + // extraVolumes is an extra set of host volumes, mounted to the control plane component. + // +optional + // +kubebuilder:validation:MaxItems=100 + ExtraVolumes []HostPathMount `json:"extraVolumes,omitempty"` + + // extraEnvs is an extra set of environment variables to pass to the control plane component. + // Environment variables passed using ExtraEnvs will override any existing environment variables, or *_proxy environment variables that kubeadm adds by default. + // This option takes effect only on Kubernetes >=1.31.0. + // +optional + // +kubebuilder:validation:MaxItems=100 + ExtraEnvs []EnvVar `json:"extraEnvs,omitempty"` +} + +// APIServer holds settings necessary for API server deployments in the cluster. +type APIServer struct { + ControlPlaneComponent `json:",inline"` + + // certSANs sets extra Subject Alternative Names for the API Server signing cert. + // +optional + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=253 + CertSANs []string `json:"certSANs,omitempty"` + + // timeoutForControlPlane controls the timeout that we use for API server to appear + // +optional + TimeoutForControlPlane *metav1.Duration `json:"timeoutForControlPlane,omitempty"` +} + +// DNS defines the DNS addon that should be used in the cluster. +type DNS struct { + // ImageMeta allows to customize the image used for the DNS component + ImageMeta `json:",inline"` +} + +// ImageMeta allows to customize the image used for components that are not +// originated from the Kubernetes/Kubernetes release process. +type ImageMeta struct { + // imageRepository sets the container registry to pull images from. + // if not set, the ImageRepository defined in ClusterConfiguration will be used instead. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + ImageRepository string `json:"imageRepository,omitempty"` + + // imageTag allows to specify a tag for the image. + // In case this value is set, kubeadm does not change automatically the version of the above components during upgrades. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + ImageTag string `json:"imageTag,omitempty"` + + // TODO: evaluate if we need also a ImageName based on user feedbacks +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterStatus contains the cluster status. The ClusterStatus will be stored in the kubeadm-config +// ConfigMap in the cluster, and then updated by kubeadm when additional control plane instance joins or leaves the cluster. +// +// Deprecated: ClusterStatus has been removed from kubeadm v1beta3 API; This type is preserved only to support +// conversion to older versions of the kubeadm API. +type ClusterStatus struct { + metav1.TypeMeta `json:",inline"` + + // apiEndpoints currently available in the cluster, one for each control plane/api server instance. + // The key of the map is the IP of the host's default interface + // +optional + APIEndpoints map[string]APIEndpoint `json:"apiEndpoints"` +} + +// APIEndpoint struct contains elements of API server instance deployed on a node. +type APIEndpoint struct { + // advertiseAddress sets the IP address for the API server to advertise. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=39 + AdvertiseAddress string `json:"advertiseAddress,omitempty"` + + // bindPort sets the secure port for the API Server to bind to. + // Defaults to 6443. + // +optional + BindPort int32 `json:"bindPort,omitempty"` +} + +// NodeRegistrationOptions holds fields that relate to registering a new control-plane or node to the cluster, either via "kubeadm init" or "kubeadm join". +// Note: The NodeRegistrationOptions struct has to be kept in sync with the structs in MarshalJSON. +type NodeRegistrationOptions struct { + + // name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm join` operation. + // This field is also used in the CommonName field of the kubelet's client certificate to the API server. + // Defaults to the hostname of the node if not provided. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Name string `json:"name,omitempty"` + + // criSocket is used to retrieve container runtime info. This information will be annotated to the Node API object, for later re-use + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + CRISocket string `json:"criSocket,omitempty"` + + // taints specifies the taints the Node API object should be registered with. If this field is unset, i.e. nil, in the `kubeadm init` process + // it will be defaulted to []v1.Taint{'node-role.kubernetes.io/master=""'}. If you don't want to taint your control-plane node, set this field to an + // empty slice, i.e. `taints: []` in the YAML file. This field is solely used for Node registration. + // +optional + // +kubebuilder:validation:MaxItems=100 + Taints []corev1.Taint `json:"taints,omitempty"` + + // kubeletExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file + // kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap + // Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. + // +optional + KubeletExtraArgs map[string]string `json:"kubeletExtraArgs,omitempty"` + + // ignorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered. + // +optional + // +kubebuilder:validation:MaxItems=50 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=512 + IgnorePreflightErrors []string `json:"ignorePreflightErrors,omitempty"` + + // imagePullPolicy specifies the policy for image pulling + // during kubeadm "init" and "join" operations. The value of + // this field must be one of "Always", "IfNotPresent" or + // "Never". Defaults to "IfNotPresent". This can be used only + // with Kubernetes version equal to 1.22 and later. + // +kubebuilder:validation:Enum=Always;IfNotPresent;Never + // +optional + ImagePullPolicy string `json:"imagePullPolicy,omitempty"` + + // imagePullSerial specifies if image pulling performed by kubeadm must be done serially or in parallel. + // This option takes effect only on Kubernetes >=1.31.0. + // Default: true (defaulted in kubeadm) + // +optional + ImagePullSerial *bool `json:"imagePullSerial,omitempty"` +} + +// MarshalJSON marshals NodeRegistrationOptions in a way that an empty slice in Taints is preserved. +// Taints are then rendered as: +// * nil => omitted from the marshalled JSON +// * [] => rendered as empty array (`[]`) +// * [regular-array] => rendered as usual +// We have to do this as the regular Golang JSON marshalling would just omit +// the empty slice (xref: https://github.com/golang/go/issues/22480). +// Note: We can't re-use the original struct as that would lead to an infinite recursion. +// Note: The structs in this func have to be kept in sync with the NodeRegistrationOptions struct. +func (n *NodeRegistrationOptions) MarshalJSON() ([]byte, error) { + // Marshal an empty Taints slice array without omitempty so it's preserved. + if n.Taints != nil && len(n.Taints) == 0 { + return json.Marshal(struct { + Name string `json:"name,omitempty"` + CRISocket string `json:"criSocket,omitempty"` + Taints []corev1.Taint `json:"taints"` + KubeletExtraArgs map[string]string `json:"kubeletExtraArgs,omitempty"` + IgnorePreflightErrors []string `json:"ignorePreflightErrors,omitempty"` + ImagePullPolicy string `json:"imagePullPolicy,omitempty"` + ImagePullSerial *bool `json:"imagePullSerial,omitempty"` + }{ + Name: n.Name, + CRISocket: n.CRISocket, + Taints: n.Taints, + KubeletExtraArgs: n.KubeletExtraArgs, + IgnorePreflightErrors: n.IgnorePreflightErrors, + ImagePullPolicy: n.ImagePullPolicy, + ImagePullSerial: n.ImagePullSerial, + }) + } + + // If Taints is nil or not empty we can use omitempty. + return json.Marshal(struct { + Name string `json:"name,omitempty"` + CRISocket string `json:"criSocket,omitempty"` + Taints []corev1.Taint `json:"taints,omitempty"` + KubeletExtraArgs map[string]string `json:"kubeletExtraArgs,omitempty"` + IgnorePreflightErrors []string `json:"ignorePreflightErrors,omitempty"` + ImagePullPolicy string `json:"imagePullPolicy,omitempty"` + ImagePullSerial *bool `json:"imagePullSerial,omitempty"` + }{ + Name: n.Name, + CRISocket: n.CRISocket, + Taints: n.Taints, + KubeletExtraArgs: n.KubeletExtraArgs, + IgnorePreflightErrors: n.IgnorePreflightErrors, + ImagePullPolicy: n.ImagePullPolicy, + ImagePullSerial: n.ImagePullSerial, + }) +} + +// Networking contains elements describing cluster's networking configuration. +type Networking struct { + // serviceSubnet is the subnet used by k8s services. + // Defaults to a comma-delimited string of the Cluster object's spec.clusterNetwork.pods.cidrBlocks, or + // to "10.96.0.0/12" if that's unset. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=1024 + ServiceSubnet string `json:"serviceSubnet,omitempty"` + // podSubnet is the subnet used by pods. + // If unset, the API server will not allocate CIDR ranges for every node. + // Defaults to a comma-delimited string of the Cluster object's spec.clusterNetwork.services.cidrBlocks if that is set + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=1024 + PodSubnet string `json:"podSubnet,omitempty"` + // dnsDomain is the dns domain used by k8s services. Defaults to "cluster.local". + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + DNSDomain string `json:"dnsDomain,omitempty"` +} + +// BootstrapToken describes one bootstrap token, stored as a Secret in the cluster. +type BootstrapToken struct { + // token is used for establishing bidirectional trust between nodes and control-planes. + // Used for joining nodes in the cluster. + // +required + Token *BootstrapTokenString `json:"token"` + // description sets a human-friendly message why this token exists and what it's used + // for, so other administrators can know its purpose. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + Description string `json:"description,omitempty"` + // ttl defines the time to live for this token. Defaults to 24h. + // Expires and TTL are mutually exclusive. + // +optional + TTL *metav1.Duration `json:"ttl,omitempty"` + // expires specifies the timestamp when this token expires. Defaults to being set + // dynamically at runtime based on the TTL. Expires and TTL are mutually exclusive. + // +optional + Expires *metav1.Time `json:"expires,omitempty"` + // usages describes the ways in which this token can be used. Can by default be used + // for establishing bidirectional trust, but that can be changed here. + // +optional + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=256 + Usages []string `json:"usages,omitempty"` + // groups specifies the extra groups that this token will authenticate as when/if + // used for authentication + // +optional + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=256 + Groups []string `json:"groups,omitempty"` +} + +// Etcd contains elements describing Etcd configuration. +type Etcd struct { + + // local provides configuration knobs for configuring the local etcd instance + // Local and External are mutually exclusive + // +optional + Local *LocalEtcd `json:"local,omitempty"` + + // external describes how to connect to an external etcd cluster + // Local and External are mutually exclusive + // +optional + External *ExternalEtcd `json:"external,omitempty"` +} + +// LocalEtcd describes that kubeadm should run an etcd cluster locally. +type LocalEtcd struct { + // ImageMeta allows to customize the container used for etcd + ImageMeta `json:",inline"` + + // dataDir is the directory etcd will place its data. + // Defaults to "/var/lib/etcd". + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + DataDir string `json:"dataDir,omitempty"` + + // extraArgs are extra arguments provided to the etcd binary + // when run inside a static pod. + // +optional + ExtraArgs map[string]string `json:"extraArgs,omitempty"` + + // extraEnvs is an extra set of environment variables to pass to the control plane component. + // Environment variables passed using ExtraEnvs will override any existing environment variables, or *_proxy environment variables that kubeadm adds by default. + // This option takes effect only on Kubernetes >=1.31.0. + // +optional + // +kubebuilder:validation:MaxItems=100 + ExtraEnvs []EnvVar `json:"extraEnvs,omitempty"` + + // serverCertSANs sets extra Subject Alternative Names for the etcd server signing cert. + // +optional + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=253 + ServerCertSANs []string `json:"serverCertSANs,omitempty"` + + // peerCertSANs sets extra Subject Alternative Names for the etcd peer signing cert. + // +optional + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=253 + PeerCertSANs []string `json:"peerCertSANs,omitempty"` +} + +// ExternalEtcd describes an external etcd cluster. +// Kubeadm has no knowledge of where certificate files live and they must be supplied. +type ExternalEtcd struct { + // endpoints of etcd members. Required for ExternalEtcd. + // +required + // +kubebuilder:validation:MaxItems=50 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=512 + Endpoints []string `json:"endpoints"` + + // caFile is an SSL Certificate Authority file used to secure etcd communication. + // Required if using a TLS connection. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + CAFile string `json:"caFile"` + + // certFile is an SSL certification file used to secure etcd communication. + // Required if using a TLS connection. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + CertFile string `json:"certFile"` + + // keyFile is an SSL key file used to secure etcd communication. + // Required if using a TLS connection. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + KeyFile string `json:"keyFile"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// JoinConfiguration contains elements describing a particular node. +type JoinConfiguration struct { + metav1.TypeMeta `json:",inline"` + + // nodeRegistration holds fields that relate to registering the new control-plane node to the cluster. + // When used in the context of control plane nodes, NodeRegistration should remain consistent + // across both InitConfiguration and JoinConfiguration + // +optional + NodeRegistration NodeRegistrationOptions `json:"nodeRegistration,omitempty"` + + // caCertPath is the path to the SSL certificate authority used to + // secure comunications between node and control-plane. + // Defaults to "/etc/kubernetes/pki/ca.crt". + // +optional + // TODO: revisit when there is defaulting from k/k + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + CACertPath string `json:"caCertPath,omitempty"` + + // discovery specifies the options for the kubelet to use during the TLS Bootstrap process + // +optional + // TODO: revisit when there is defaulting from k/k + Discovery Discovery `json:"discovery,omitempty"` + + // controlPlane defines the additional control plane instance to be deployed on the joining node. + // If nil, no additional control plane instance will be deployed. + // +optional + ControlPlane *JoinControlPlane `json:"controlPlane,omitempty"` + + // skipPhases is a list of phases to skip during command execution. + // The list of phases can be obtained with the "kubeadm init --help" command. + // This option takes effect only on Kubernetes >=1.22.0. + // +optional + // +kubebuilder:validation:MaxItems=50 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=256 + SkipPhases []string `json:"skipPhases,omitempty"` + + // patches contains options related to applying patches to components deployed by kubeadm during + // "kubeadm join". The minimum kubernetes version needed to support Patches is v1.22 + // +optional + Patches *Patches `json:"patches,omitempty"` +} + +// JoinControlPlane contains elements describing an additional control plane instance to be deployed on the joining node. +type JoinControlPlane struct { + // localAPIEndpoint represents the endpoint of the API server instance to be deployed on this node. + // +optional + LocalAPIEndpoint APIEndpoint `json:"localAPIEndpoint,omitempty"` +} + +// Discovery specifies the options for the kubelet to use during the TLS Bootstrap process. +type Discovery struct { + // bootstrapToken is used to set the options for bootstrap token based discovery + // BootstrapToken and File are mutually exclusive + // +optional + BootstrapToken *BootstrapTokenDiscovery `json:"bootstrapToken,omitempty"` + + // file is used to specify a file or URL to a kubeconfig file from which to load cluster information + // BootstrapToken and File are mutually exclusive + // +optional + File *FileDiscovery `json:"file,omitempty"` + + // tlsBootstrapToken is a token used for TLS bootstrapping. + // If .BootstrapToken is set, this field is defaulted to .BootstrapToken.Token, but can be overridden. + // If .File is set, this field **must be set** in case the KubeConfigFile does not contain any other authentication information + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + TLSBootstrapToken string `json:"tlsBootstrapToken,omitempty"` + + // timeout modifies the discovery timeout + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` +} + +// BootstrapTokenDiscovery is used to set the options for bootstrap token based discovery. +type BootstrapTokenDiscovery struct { + // token is a token used to validate cluster information + // fetched from the control-plane. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + Token string `json:"token,omitempty"` + + // apiServerEndpoint is an IP or domain name to the API server from which info will be fetched. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + APIServerEndpoint string `json:"apiServerEndpoint,omitempty"` + + // caCertHashes specifies a set of public key pins to verify + // when token-based discovery is used. The root CA found during discovery + // must match one of these values. Specifying an empty set disables root CA + // pinning, which can be unsafe. Each hash is specified as ":", + // where the only currently supported type is "sha256". This is a hex-encoded + // SHA-256 hash of the Subject Public Key Info (SPKI) object in DER-encoded + // ASN.1. These hashes can be calculated using, for example, OpenSSL: + // openssl x509 -pubkey -in ca.crt openssl rsa -pubin -outform der 2>&/dev/null | openssl dgst -sha256 -hex + // +optional + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=512 + CACertHashes []string `json:"caCertHashes,omitempty"` + + // unsafeSkipCAVerification allows token-based discovery + // without CA verification via CACertHashes. This can weaken + // the security of kubeadm since other nodes can impersonate the control-plane. + // +optional + UnsafeSkipCAVerification bool `json:"unsafeSkipCAVerification,omitempty"` +} + +// FileDiscovery is used to specify a file or URL to a kubeconfig file from which to load cluster information. +type FileDiscovery struct { + // kubeConfigPath is used to specify the actual file path or URL to the kubeconfig file from which to load cluster information + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + KubeConfigPath string `json:"kubeConfigPath"` + + // kubeConfig is used (optionally) to generate a KubeConfig based on the KubeadmConfig's information. + // The file is generated at the path specified in KubeConfigPath. + // + // Host address (server field) information is automatically populated based on the Cluster's ControlPlaneEndpoint. + // Certificate Authority (certificate-authority-data field) is gathered from the cluster's CA secret. + // + // +optional + KubeConfig *FileDiscoveryKubeConfig `json:"kubeConfig,omitempty"` +} + +// FileDiscoveryKubeConfig contains elements describing how to generate the kubeconfig for bootstrapping. +type FileDiscoveryKubeConfig struct { + // cluster contains information about how to communicate with the kubernetes cluster. + // + // By default the following fields are automatically populated: + // - Server with the Cluster's ControlPlaneEndpoint. + // - CertificateAuthorityData with the Cluster's CA certificate. + // +optional + Cluster *KubeConfigCluster `json:"cluster,omitempty"` + + // user contains information that describes identity information. + // This is used to tell the kubernetes cluster who you are. + // +required + User KubeConfigUser `json:"user"` +} + +// KubeConfigCluster contains information about how to communicate with a kubernetes cluster. +// +// Adapted from clientcmdv1.Cluster. +type KubeConfigCluster struct { + // server is the address of the kubernetes cluster (https://hostname:port). + // + // Defaults to https:// + Cluster.Spec.ControlPlaneEndpoint. + // + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + Server string `json:"server,omitempty"` + + // tlsServerName is used to check server certificate. If TLSServerName is empty, the hostname used to contact the server is used. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + TLSServerName string `json:"tlsServerName,omitempty"` + + // insecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure. + // +optional + InsecureSkipTLSVerify bool `json:"insecureSkipTLSVerify,omitempty"` + + // certificateAuthorityData contains PEM-encoded certificate authority certificates. + // + // Defaults to the Cluster's CA certificate if empty. + // + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=51200 + CertificateAuthorityData []byte `json:"certificateAuthorityData,omitempty"` + + // proxyURL is the URL to the proxy to be used for all requests made by this + // client. URLs with "http", "https", and "socks5" schemes are supported. If + // this configuration is not provided or the empty string, the client + // attempts to construct a proxy configuration from http_proxy and + // https_proxy environment variables. If these environment variables are not + // set, the client does not attempt to proxy requests. + // + // socks5 proxying does not currently support spdy streaming endpoints (exec, + // attach, port forward). + // + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + ProxyURL string `json:"proxyURL,omitempty"` +} + +// KubeConfigUser contains information that describes identity information. +// This is used to tell the kubernetes cluster who you are. +// +// Either authProvider or exec must be filled. +// +// Adapted from clientcmdv1.AuthInfo. +type KubeConfigUser struct { + // authProvider specifies a custom authentication plugin for the kubernetes cluster. + // +optional + AuthProvider *KubeConfigAuthProvider `json:"authProvider,omitempty"` + + // exec specifies a custom exec-based authentication plugin for the kubernetes cluster. + // +optional + Exec *KubeConfigAuthExec `json:"exec,omitempty"` +} + +// KubeConfigAuthProvider holds the configuration for a specified auth provider. +type KubeConfigAuthProvider struct { + // name is the name of the authentication plugin. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Name string `json:"name"` + + // config holds the parameters for the authentication plugin. + // +optional + Config map[string]string `json:"config,omitempty"` +} + +// KubeConfigAuthExec specifies a command to provide client credentials. The command is exec'd +// and outputs structured stdout holding credentials. +// +// See the client.authentication.k8s.io API group for specifications of the exact input +// and output format. +type KubeConfigAuthExec struct { + // command to execute. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=1024 + Command string `json:"command"` + + // args is the arguments to pass to the command when executing it. + // +optional + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=512 + Args []string `json:"args,omitempty"` + + // env defines additional environment variables to expose to the process. These + // are unioned with the host's environment, as well as variables client-go uses + // to pass argument to the plugin. + // +optional + // +kubebuilder:validation:MaxItems=100 + Env []KubeConfigAuthExecEnv `json:"env,omitempty"` + + // apiVersion is preferred input version of the ExecInfo. The returned ExecCredentials MUST use + // the same encoding version as the input. + // Defaults to client.authentication.k8s.io/v1 if not set. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + APIVersion string `json:"apiVersion,omitempty"` + + // provideClusterInfo determines whether or not to provide cluster information, + // which could potentially contain very large CA data, to this exec plugin as a + // part of the KUBERNETES_EXEC_INFO environment variable. By default, it is set + // to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for + // reading this environment variable. + // +optional + ProvideClusterInfo bool `json:"provideClusterInfo,omitempty"` +} + +// KubeConfigAuthExecEnv is used for setting environment variables when executing an exec-based +// credential plugin. +type KubeConfigAuthExecEnv struct { + // name of the environment variable + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + Name string `json:"name"` + // value of the environment variable + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + Value string `json:"value"` +} + +// HostPathMount contains elements describing volumes that are mounted from the +// host. +type HostPathMount struct { + // name of the volume inside the pod template. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + Name string `json:"name"` + // hostPath is the path in the host that will be mounted inside + // the pod. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + HostPath string `json:"hostPath"` + // mountPath is the path inside the pod where hostPath will be mounted. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + MountPath string `json:"mountPath"` + // readOnly controls write access to the volume + // +optional + ReadOnly bool `json:"readOnly,omitempty"` + // pathType is the type of the HostPath. + // +optional + PathType corev1.HostPathType `json:"pathType,omitempty"` +} + +// BootstrapTokenString is a token of the format abcdef.abcdef0123456789 that is used +// for both validation of the practically of the API server from a joining node's point +// of view and as an authentication method for the node in the bootstrap phase of +// "kubeadm join". This token is and should be short-lived. +// +// +kubebuilder:validation:Type=string +type BootstrapTokenString struct { + ID string `json:"-"` + Secret string `json:"-"` +} + +// MarshalJSON implements the json.Marshaler interface. +func (bts BootstrapTokenString) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("%q", bts.String())), nil +} + +// UnmarshalJSON implements the json.Unmarshaller interface. +func (bts *BootstrapTokenString) UnmarshalJSON(b []byte) error { + // If the token is represented as "", just return quickly without an error + if len(b) == 0 { + return nil + } + + // Remove unnecessary " characters coming from the JSON parser + token := strings.ReplaceAll(string(b), `"`, ``) + // Convert the string Token to a BootstrapTokenString object + newbts, err := NewBootstrapTokenString(token) + if err != nil { + return err + } + bts.ID = newbts.ID + bts.Secret = newbts.Secret + return nil +} + +// String returns the string representation of the BootstrapTokenString. +func (bts BootstrapTokenString) String() string { + if bts.ID != "" && bts.Secret != "" { + return bootstraputil.TokenFromIDAndSecret(bts.ID, bts.Secret) + } + return "" +} + +// NewBootstrapTokenString converts the given Bootstrap Token as a string +// to the BootstrapTokenString object used for serialization/deserialization +// and internal usage. It also automatically validates that the given token +// is of the right format. +func NewBootstrapTokenString(token string) (*BootstrapTokenString, error) { + substrs := bootstraputil.BootstrapTokenRegexp.FindStringSubmatch(token) + // TODO: Add a constant for the 3 value here, and explain better why it's needed (other than because how the regexp parsin works) + if len(substrs) != 3 { + return nil, errors.Errorf("the bootstrap token %q was not of the form %q", token, bootstrapapi.BootstrapTokenPattern) + } + + return &BootstrapTokenString{ID: substrs[1], Secret: substrs[2]}, nil +} + +// Patches contains options related to applying patches to components deployed by kubeadm. +type Patches struct { + // directory is a path to a directory that contains files named "target[suffix][+patchtype].extension". + // For example, "kube-apiserver0+merge.yaml" or just "etcd.json". "target" can be one of + // "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd". "patchtype" can be one + // of "strategic" "merge" or "json" and they match the patch formats supported by kubectl. + // The default "patchtype" is "strategic". "extension" must be either "json" or "yaml". + // "suffix" is an optional string that can be used to determine which patches are applied + // first alpha-numerically. + // These files can be written into the target directory via KubeadmConfig.Files which + // specifies additional files to be created on the machine, either with content inline or + // by referencing a secret. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + Directory string `json:"directory,omitempty"` +} + +// EnvVar represents an environment variable present in a Container. +type EnvVar struct { + corev1.EnvVar `json:",inline"` +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/kubeadmconfig_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/kubeadmconfig_types.go new file mode 100644 index 0000000000..98a582479b --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/kubeadmconfig_types.go @@ -0,0 +1,850 @@ +/* +Copyright 2021 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 v1beta1 + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" + + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" + "sigs.k8s.io/cluster-api/feature" +) + +// Format specifies the output format of the bootstrap data +// +kubebuilder:validation:Enum=cloud-config;ignition +type Format string + +const ( + // CloudConfig make the bootstrap data to be of cloud-config format. + CloudConfig Format = "cloud-config" + + // Ignition make the bootstrap data to be of Ignition format. + Ignition Format = "ignition" +) + +var ( + cannotUseWithIgnition = fmt.Sprintf("not supported when spec.format is set to: %q", Ignition) + conflictingFileSourceMsg = "only one of content or contentFrom may be specified for a single file" + conflictingUserSourceMsg = "only one of passwd or passwdFrom may be specified for a single user" + kubeadmBootstrapFormatIgnitionFeatureDisabledMsg = "can be set only if the KubeadmBootstrapFormatIgnition feature gate is enabled" + missingSecretNameMsg = "secret file source must specify non-empty secret name" + missingSecretKeyMsg = "secret file source must specify non-empty secret key" + pathConflictMsg = "path property must be unique among all files" +) + +// KubeadmConfigSpec defines the desired state of KubeadmConfig. +// Either ClusterConfiguration and InitConfiguration should be defined or the JoinConfiguration should be defined. +type KubeadmConfigSpec struct { + // clusterConfiguration along with InitConfiguration are the configurations necessary for the init command + // +optional + ClusterConfiguration *ClusterConfiguration `json:"clusterConfiguration,omitempty"` + + // initConfiguration along with ClusterConfiguration are the configurations necessary for the init command + // +optional + InitConfiguration *InitConfiguration `json:"initConfiguration,omitempty"` + + // joinConfiguration is the kubeadm configuration for the join command + // +optional + JoinConfiguration *JoinConfiguration `json:"joinConfiguration,omitempty"` + + // files specifies extra files to be passed to user_data upon creation. + // +optional + // +kubebuilder:validation:MaxItems=200 + Files []File `json:"files,omitempty"` + + // diskSetup specifies options for the creation of partition tables and file systems on devices. + // +optional + DiskSetup *DiskSetup `json:"diskSetup,omitempty"` + + // mounts specifies a list of mount points to be setup. + // +optional + // +kubebuilder:validation:MaxItems=100 + Mounts []MountPoints `json:"mounts,omitempty"` + + // bootCommands specifies extra commands to run very early in the boot process via the cloud-init bootcmd + // module. bootcmd will run on every boot, 'cloud-init-per' command can be used to make bootcmd run exactly + // once. This is typically run in the cloud-init.service systemd unit. This has no effect in Ignition. + // +optional + // +kubebuilder:validation:MaxItems=1000 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=10240 + BootCommands []string `json:"bootCommands,omitempty"` + + // preKubeadmCommands specifies extra commands to run before kubeadm runs. + // With cloud-init, this is prepended to the runcmd module configuration, and is typically executed in + // the cloud-final.service systemd unit. In Ignition, this is prepended to /etc/kubeadm.sh. + // +optional + // +kubebuilder:validation:MaxItems=1000 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=10240 + PreKubeadmCommands []string `json:"preKubeadmCommands,omitempty"` + + // postKubeadmCommands specifies extra commands to run after kubeadm runs. + // With cloud-init, this is appended to the runcmd module configuration, and is typically executed in + // the cloud-final.service systemd unit. In Ignition, this is appended to /etc/kubeadm.sh. + // +optional + // +kubebuilder:validation:MaxItems=1000 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=10240 + PostKubeadmCommands []string `json:"postKubeadmCommands,omitempty"` + + // users specifies extra users to add + // +optional + // +kubebuilder:validation:MaxItems=100 + Users []User `json:"users,omitempty"` + + // ntp specifies NTP configuration + // +optional + NTP *NTP `json:"ntp,omitempty"` + + // format specifies the output format of the bootstrap data + // +optional + Format Format `json:"format,omitempty"` + + // verbosity is the number for the kubeadm log level verbosity. + // It overrides the `--v` flag in kubeadm commands. + // +optional + Verbosity *int32 `json:"verbosity,omitempty"` + + // useExperimentalRetryJoin replaces a basic kubeadm command with a shell + // script with retries for joins. + // + // This is meant to be an experimental temporary workaround on some environments + // where joins fail due to timing (and other issues). The long term goal is to add retries to + // kubeadm proper and use that functionality. + // + // This will add about 40KB to userdata + // + // For more information, refer to https://github.com/kubernetes-sigs/cluster-api/pull/2763#discussion_r397306055. + // +optional + // + // Deprecated: This experimental fix is no longer needed and this field will be removed in a future release. + // When removing also remove from staticcheck exclude-rules for SA1019 in golangci.yml + UseExperimentalRetryJoin bool `json:"useExperimentalRetryJoin,omitempty"` + + // ignition contains Ignition specific configuration. + // +optional + Ignition *IgnitionSpec `json:"ignition,omitempty"` +} + +// Default defaults a KubeadmConfigSpec. +func (c *KubeadmConfigSpec) Default() { + if c.Format == "" { + c.Format = CloudConfig + } + if c.InitConfiguration != nil && c.InitConfiguration.NodeRegistration.ImagePullPolicy == "" { + c.InitConfiguration.NodeRegistration.ImagePullPolicy = "IfNotPresent" + } + if c.JoinConfiguration != nil && c.JoinConfiguration.NodeRegistration.ImagePullPolicy == "" { + c.JoinConfiguration.NodeRegistration.ImagePullPolicy = "IfNotPresent" + } + if c.JoinConfiguration != nil && c.JoinConfiguration.Discovery.File != nil { + if kfg := c.JoinConfiguration.Discovery.File.KubeConfig; kfg != nil { + if kfg.User.Exec != nil { + if kfg.User.Exec.APIVersion == "" { + kfg.User.Exec.APIVersion = "client.authentication.k8s.io/v1" + } + } + } + } +} + +// Validate ensures the KubeadmConfigSpec is valid. +func (c *KubeadmConfigSpec) Validate(pathPrefix *field.Path) field.ErrorList { + var allErrs field.ErrorList + + allErrs = append(allErrs, c.validateFiles(pathPrefix)...) + allErrs = append(allErrs, c.validateUsers(pathPrefix)...) + allErrs = append(allErrs, c.validateIgnition(pathPrefix)...) + + // Validate JoinConfiguration. + if c.JoinConfiguration != nil { + if c.JoinConfiguration.Discovery.File != nil { + if kfg := c.JoinConfiguration.Discovery.File.KubeConfig; kfg != nil { + userPath := pathPrefix.Child("joinConfiguration", "discovery", "file", "kubeconfig", "user") + if kfg.User.AuthProvider == nil && kfg.User.Exec == nil { + allErrs = append(allErrs, + field.Invalid( + userPath, + kfg.User, + "at least one of authProvider or exec must be defined", + ), + ) + } + if kfg.User.AuthProvider != nil && kfg.User.Exec != nil { + allErrs = append(allErrs, + field.Invalid( + userPath, + kfg.User, + "either authProvider or exec must be defined", + ), + ) + } + } + } + } + + return allErrs +} + +func (c *KubeadmConfigSpec) validateFiles(pathPrefix *field.Path) field.ErrorList { + var allErrs field.ErrorList + + knownPaths := map[string]struct{}{} + + for i := range c.Files { + file := c.Files[i] + if file.Content != "" && file.ContentFrom != nil { + allErrs = append( + allErrs, + field.Invalid( + pathPrefix.Child("files").Index(i), + file, + conflictingFileSourceMsg, + ), + ) + } + // n.b.: if we ever add types besides Secret as a ContentFrom + // Source, we must add webhook validation here for one of the + // sources being non-nil. + if file.ContentFrom != nil { + if file.ContentFrom.Secret.Name == "" { + allErrs = append( + allErrs, + field.Required( + pathPrefix.Child("files").Index(i).Child("contentFrom", "secret", "name"), + missingSecretNameMsg, + ), + ) + } + if file.ContentFrom.Secret.Key == "" { + allErrs = append( + allErrs, + field.Required( + pathPrefix.Child("files").Index(i).Child("contentFrom", "secret", "key"), + missingSecretKeyMsg, + ), + ) + } + } + _, conflict := knownPaths[file.Path] + if conflict { + allErrs = append( + allErrs, + field.Invalid( + pathPrefix.Child("files").Index(i).Child("path"), + file, + pathConflictMsg, + ), + ) + } + knownPaths[file.Path] = struct{}{} + } + + return allErrs +} + +func (c *KubeadmConfigSpec) validateUsers(pathPrefix *field.Path) field.ErrorList { + var allErrs field.ErrorList + + for i := range c.Users { + user := c.Users[i] + if user.Passwd != nil && user.PasswdFrom != nil { + allErrs = append( + allErrs, + field.Invalid( + pathPrefix.Child("users").Index(i), + user, + conflictingUserSourceMsg, + ), + ) + } + // n.b.: if we ever add types besides Secret as a PasswdFrom + // Source, we must add webhook validation here for one of the + // sources being non-nil. + if user.PasswdFrom != nil { + if user.PasswdFrom.Secret.Name == "" { + allErrs = append( + allErrs, + field.Required( + pathPrefix.Child("users").Index(i).Child("passwdFrom", "secret", "name"), + missingSecretNameMsg, + ), + ) + } + if user.PasswdFrom.Secret.Key == "" { + allErrs = append( + allErrs, + field.Required( + pathPrefix.Child("users").Index(i).Child("passwdFrom", "secret", "key"), + missingSecretKeyMsg, + ), + ) + } + } + } + + return allErrs +} + +func (c *KubeadmConfigSpec) validateIgnition(pathPrefix *field.Path) field.ErrorList { + var allErrs field.ErrorList + + if !feature.Gates.Enabled(feature.KubeadmBootstrapFormatIgnition) { + if c.Format == Ignition { + allErrs = append(allErrs, field.Forbidden( + pathPrefix.Child("format"), kubeadmBootstrapFormatIgnitionFeatureDisabledMsg)) + } + + if c.Ignition != nil { + allErrs = append(allErrs, field.Forbidden( + pathPrefix.Child("ignition"), kubeadmBootstrapFormatIgnitionFeatureDisabledMsg)) + } + + return allErrs + } + + if c.Format != Ignition { + if c.Ignition != nil { + allErrs = append( + allErrs, + field.Invalid( + pathPrefix.Child("format"), + c.Format, + fmt.Sprintf("must be set to %q if spec.ignition is set", Ignition), + ), + ) + } + + return allErrs + } + + for i, user := range c.Users { + if user.Inactive != nil && *user.Inactive { + allErrs = append( + allErrs, + field.Forbidden( + pathPrefix.Child("users").Index(i).Child("inactive"), + cannotUseWithIgnition, + ), + ) + } + } + + if c.UseExperimentalRetryJoin { + allErrs = append( + allErrs, + field.Forbidden( + pathPrefix.Child("useExperimentalRetryJoin"), + cannotUseWithIgnition, + ), + ) + } + + for i, file := range c.Files { + if file.Encoding == Gzip || file.Encoding == GzipBase64 { + allErrs = append( + allErrs, + field.Forbidden( + pathPrefix.Child("files").Index(i).Child("encoding"), + cannotUseWithIgnition, + ), + ) + } + } + + if c.BootCommands != nil { + allErrs = append( + allErrs, + field.Forbidden( + pathPrefix.Child("bootCommands"), + cannotUseWithIgnition, + ), + ) + } + + if c.DiskSetup == nil { + return allErrs + } + + for i, partition := range c.DiskSetup.Partitions { + if partition.TableType != nil && *partition.TableType != "gpt" { + allErrs = append( + allErrs, + field.Invalid( + pathPrefix.Child("diskSetup", "partitions").Index(i).Child("tableType"), + *partition.TableType, + fmt.Sprintf( + "only partition type %q is supported when spec.format is set to %q", + "gpt", + Ignition, + ), + ), + ) + } + } + + for i, fs := range c.DiskSetup.Filesystems { + if fs.ReplaceFS != nil { + allErrs = append( + allErrs, + field.Forbidden( + pathPrefix.Child("diskSetup", "filesystems").Index(i).Child("replaceFS"), + cannotUseWithIgnition, + ), + ) + } + + if fs.Partition != nil { + allErrs = append( + allErrs, + field.Forbidden( + pathPrefix.Child("diskSetup", "filesystems").Index(i).Child("partition"), + cannotUseWithIgnition, + ), + ) + } + } + + return allErrs +} + +// IgnitionSpec contains Ignition specific configuration. +type IgnitionSpec struct { + // containerLinuxConfig contains CLC specific configuration. + // +optional + ContainerLinuxConfig *ContainerLinuxConfig `json:"containerLinuxConfig,omitempty"` +} + +// ContainerLinuxConfig contains CLC-specific configuration. +// +// We use a structured type here to allow adding additional fields, for example 'version'. +type ContainerLinuxConfig struct { + // additionalConfig contains additional configuration to be merged with the Ignition + // configuration generated by the bootstrapper controller. More info: https://coreos.github.io/ignition/operator-notes/#config-merging + // + // The data format is documented here: https://kinvolk.io/docs/flatcar-container-linux/latest/provisioning/cl-config/ + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=32768 + AdditionalConfig string `json:"additionalConfig,omitempty"` + + // strict controls if AdditionalConfig should be strictly parsed. If so, warnings are treated as errors. + // +optional + Strict bool `json:"strict,omitempty"` +} + +// KubeadmConfigStatus defines the observed state of KubeadmConfig. +type KubeadmConfigStatus struct { + // ready indicates the BootstrapData field is ready to be consumed + // +optional + Ready bool `json:"ready"` + + // dataSecretName is the name of the secret that stores the bootstrap data script. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + DataSecretName *string `json:"dataSecretName,omitempty"` + + // failureReason will be set on non-retryable errors + // + // Deprecated: This field is deprecated and is going to be removed when support for v1beta1 will be dropped. Please see https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more details. + // + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + FailureReason string `json:"failureReason,omitempty"` + + // failureMessage will be set on non-retryable errors + // + // Deprecated: This field is deprecated and is going to be removed when support for v1beta1 will be dropped. Please see https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more details. + // + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=10240 + FailureMessage string `json:"failureMessage,omitempty"` + + // observedGeneration is the latest generation observed by the controller. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // conditions defines current service state of the KubeadmConfig. + // +optional + Conditions clusterv1beta1.Conditions `json:"conditions,omitempty"` + + // v1beta2 groups all the fields that will be added or modified in KubeadmConfig's status with the V1Beta2 version. + // +optional + V1Beta2 *KubeadmConfigV1Beta2Status `json:"v1beta2,omitempty"` +} + +// KubeadmConfigV1Beta2Status groups all the fields that will be added or modified in KubeadmConfig with the V1Beta2 version. +// See https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more context. +type KubeadmConfigV1Beta2Status struct { + // conditions represents the observations of a KubeadmConfig's current state. + // Known condition types are Ready, DataSecretAvailable, CertificatesAvailable. + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=32 + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=kubeadmconfigs,scope=Namespaced,categories=cluster-api +// +kubebuilder:deprecatedversion +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels['cluster\\.x-k8s\\.io/cluster-name']",description="Cluster" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of KubeadmConfig" + +// KubeadmConfig is the Schema for the kubeadmconfigs API. +type KubeadmConfig struct { + metav1.TypeMeta `json:",inline"` + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // spec is the desired state of KubeadmConfig. + // +optional + Spec KubeadmConfigSpec `json:"spec,omitempty"` + // status is the observed state of KubeadmConfig. + // +optional + Status KubeadmConfigStatus `json:"status,omitempty"` +} + +// GetConditions returns the set of conditions for this object. +func (c *KubeadmConfig) GetConditions() clusterv1beta1.Conditions { + return c.Status.Conditions +} + +// SetConditions sets the conditions on this object. +func (c *KubeadmConfig) SetConditions(conditions clusterv1beta1.Conditions) { + c.Status.Conditions = conditions +} + +// GetV1Beta2Conditions returns the set of conditions for this object. +func (c *KubeadmConfig) GetV1Beta2Conditions() []metav1.Condition { + if c.Status.V1Beta2 == nil { + return nil + } + return c.Status.V1Beta2.Conditions +} + +// SetV1Beta2Conditions sets conditions for an API object. +func (c *KubeadmConfig) SetV1Beta2Conditions(conditions []metav1.Condition) { + if c.Status.V1Beta2 == nil { + c.Status.V1Beta2 = &KubeadmConfigV1Beta2Status{} + } + c.Status.V1Beta2.Conditions = conditions +} + +// +kubebuilder:object:root=true + +// KubeadmConfigList contains a list of KubeadmConfig. +type KubeadmConfigList struct { + metav1.TypeMeta `json:",inline"` + // metadata is the standard list's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#lists-and-simple-kinds + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + // items is the list of KubeadmConfigs. + Items []KubeadmConfig `json:"items"` +} + +func init() { + objectTypes = append(objectTypes, &KubeadmConfig{}, &KubeadmConfigList{}) +} + +// Encoding specifies the cloud-init file encoding. +// +kubebuilder:validation:Enum=base64;gzip;gzip+base64 +type Encoding string + +const ( + // Base64 implies the contents of the file are encoded as base64. + Base64 Encoding = "base64" + // Gzip implies the contents of the file are encoded with gzip. + Gzip Encoding = "gzip" + // GzipBase64 implies the contents of the file are first base64 encoded and then gzip encoded. + GzipBase64 Encoding = "gzip+base64" +) + +// File defines the input for generating write_files in cloud-init. +type File struct { + // path specifies the full path on disk where to store the file. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + Path string `json:"path"` + + // owner specifies the ownership of the file, e.g. "root:root". + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Owner string `json:"owner,omitempty"` + + // permissions specifies the permissions to assign to the file, e.g. "0640". + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=16 + Permissions string `json:"permissions,omitempty"` + + // encoding specifies the encoding of the file contents. + // +optional + Encoding Encoding `json:"encoding,omitempty"` + + // append specifies whether to append Content to existing file if Path exists. + // +optional + Append bool `json:"append,omitempty"` + + // content is the actual content of the file. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=10240 + Content string `json:"content,omitempty"` + + // contentFrom is a referenced source of content to populate the file. + // +optional + ContentFrom *FileSource `json:"contentFrom,omitempty"` +} + +// FileSource is a union of all possible external source types for file data. +// Only one field may be populated in any given instance. Developers adding new +// sources of data for target systems should add them here. +type FileSource struct { + // secret represents a secret that should populate this file. + // +required + Secret SecretFileSource `json:"secret"` +} + +// SecretFileSource adapts a Secret into a FileSource. +// +// The contents of the target Secret's Data field will be presented +// as files using the keys in the Data field as the file names. +type SecretFileSource struct { + // name of the secret in the KubeadmBootstrapConfig's namespace to use. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Name string `json:"name"` + + // key is the key in the secret's data map for this value. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Key string `json:"key"` +} + +// PasswdSource is a union of all possible external source types for passwd data. +// Only one field may be populated in any given instance. Developers adding new +// sources of data for target systems should add them here. +type PasswdSource struct { + // secret represents a secret that should populate this password. + // +required + Secret SecretPasswdSource `json:"secret"` +} + +// SecretPasswdSource adapts a Secret into a PasswdSource. +// +// The contents of the target Secret's Data field will be presented +// as passwd using the keys in the Data field as the file names. +type SecretPasswdSource struct { + // name of the secret in the KubeadmBootstrapConfig's namespace to use. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Name string `json:"name"` + + // key is the key in the secret's data map for this value. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Key string `json:"key"` +} + +// User defines the input for a generated user in cloud-init. +type User struct { + // name specifies the user name + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Name string `json:"name"` + + // gecos specifies the gecos to use for the user + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Gecos *string `json:"gecos,omitempty"` + + // groups specifies the additional groups for the user + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Groups *string `json:"groups,omitempty"` + + // homeDir specifies the home directory to use for the user + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + HomeDir *string `json:"homeDir,omitempty"` + + // inactive specifies whether to mark the user as inactive + // +optional + Inactive *bool `json:"inactive,omitempty"` + + // shell specifies the user's shell + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Shell *string `json:"shell,omitempty"` + + // passwd specifies a hashed password for the user + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Passwd *string `json:"passwd,omitempty"` + + // passwdFrom is a referenced source of passwd to populate the passwd. + // +optional + PasswdFrom *PasswdSource `json:"passwdFrom,omitempty"` + + // primaryGroup specifies the primary group for the user + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + PrimaryGroup *string `json:"primaryGroup,omitempty"` + + // lockPassword specifies if password login should be disabled + // +optional + LockPassword *bool `json:"lockPassword,omitempty"` + + // sudo specifies a sudo role for the user + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Sudo *string `json:"sudo,omitempty"` + + // sshAuthorizedKeys specifies a list of ssh authorized keys for the user + // +optional + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=2048 + SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"` +} + +// NTP defines input for generated ntp in cloud-init. +type NTP struct { + // servers specifies which NTP servers to use + // +optional + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=512 + Servers []string `json:"servers,omitempty"` + + // enabled specifies whether NTP should be enabled + // +optional + Enabled *bool `json:"enabled,omitempty"` +} + +// DiskSetup defines input for generated disk_setup and fs_setup in cloud-init. +type DiskSetup struct { + // partitions specifies the list of the partitions to setup. + // +optional + // +kubebuilder:validation:MaxItems=100 + Partitions []Partition `json:"partitions,omitempty"` + + // filesystems specifies the list of file systems to setup. + // +optional + // +kubebuilder:validation:MaxItems=100 + Filesystems []Filesystem `json:"filesystems,omitempty"` +} + +// Partition defines how to create and layout a partition. +type Partition struct { + // device is the name of the device. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Device string `json:"device"` + // layout specifies the device layout. + // If it is true, a single partition will be created for the entire device. + // When layout is false, it means don't partition or ignore existing partitioning. + // +required + Layout bool `json:"layout"` + // overwrite describes whether to skip checks and create the partition if a partition or filesystem is found on the device. + // Use with caution. Default is 'false'. + // +optional + Overwrite *bool `json:"overwrite,omitempty"` + // tableType specifies the tupe of partition table. The following are supported: + // 'mbr': default and setups a MS-DOS partition table + // 'gpt': setups a GPT partition table + // +optional + // +kubebuilder:validation:Enum=mbr;gpt + TableType *string `json:"tableType,omitempty"` +} + +// Filesystem defines the file systems to be created. +type Filesystem struct { + // device specifies the device name + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Device string `json:"device"` + + // filesystem specifies the file system type. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=128 + Filesystem string `json:"filesystem"` + + // label specifies the file system label to be used. If set to None, no label is used. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + Label string `json:"label,omitempty"` + + // partition specifies the partition to use. The valid options are: "auto|any", "auto", "any", "none", and , where NUM is the actual partition number. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=128 + Partition *string `json:"partition,omitempty"` + + // overwrite defines whether or not to overwrite any existing filesystem. + // If true, any pre-existing file system will be destroyed. Use with Caution. + // +optional + Overwrite *bool `json:"overwrite,omitempty"` + + // replaceFS is a special directive, used for Microsoft Azure that instructs cloud-init to replace a file system of . + // NOTE: unless you define a label, this requires the use of the 'any' partition directive. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=128 + ReplaceFS *string `json:"replaceFS,omitempty"` + + // extraOpts defined extra options to add to the command for creating the file system. + // +optional + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=256 + ExtraOpts []string `json:"extraOpts,omitempty"` +} + +// MountPoints defines input for generated mounts in cloud-init. +// +kubebuilder:validation:items:MinLength=1 +// +kubebuilder:validation:items:MaxLength=512 +type MountPoints []string diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/kubeadmconfigtemplate_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/kubeadmconfigtemplate_types.go new file mode 100644 index 0000000000..d881f59ac6 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/kubeadmconfigtemplate_types.go @@ -0,0 +1,77 @@ +/* +Copyright 2021 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 v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" +) + +// KubeadmConfigTemplateSpec defines the desired state of KubeadmConfigTemplate. +type KubeadmConfigTemplateSpec struct { + // template defines the desired state of KubeadmConfigTemplate. + // +required + Template KubeadmConfigTemplateResource `json:"template"` +} + +// KubeadmConfigTemplateResource defines the Template structure. +type KubeadmConfigTemplateResource struct { + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + ObjectMeta clusterv1beta1.ObjectMeta `json:"metadata,omitempty"` + + // spec is the desired state of KubeadmConfig. + // +optional + Spec KubeadmConfigSpec `json:"spec,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=kubeadmconfigtemplates,scope=Namespaced,categories=cluster-api +// +kubebuilder:deprecatedversion +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of KubeadmConfigTemplate" + +// KubeadmConfigTemplate is the Schema for the kubeadmconfigtemplates API. +type KubeadmConfigTemplate struct { + metav1.TypeMeta `json:",inline"` + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // spec is the desired state of KubeadmConfigTemplate. + // +optional + Spec KubeadmConfigTemplateSpec `json:"spec,omitempty"` +} + +// +kubebuilder:object:root=true + +// KubeadmConfigTemplateList contains a list of KubeadmConfigTemplate. +type KubeadmConfigTemplateList struct { + metav1.TypeMeta `json:",inline"` + // metadata is the standard list's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#lists-and-simple-kinds + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + // items is the list of KubeadmConfigTemplates. + Items []KubeadmConfigTemplate `json:"items"` +} + +func init() { + objectTypes = append(objectTypes, &KubeadmConfigTemplate{}, &KubeadmConfigTemplateList{}) +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/v1beta2_condition_consts.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/v1beta2_condition_consts.go new file mode 100644 index 0000000000..58e121519e --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/v1beta2_condition_consts.go @@ -0,0 +1,63 @@ +/* +Copyright 2024 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 v1beta1 + +import ( + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" +) + +// KubeadmConfig's Ready condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmConfigReadyV1Beta2Condition is true if the KubeadmConfig is not deleted, + // and both DataSecretCreated, CertificatesAvailable conditions are true. + KubeadmConfigReadyV1Beta2Condition = clusterv1beta1.ReadyV1Beta2Condition + + // KubeadmConfigReadyV1Beta2Reason surfaces when the KubeadmConfig is ready. + KubeadmConfigReadyV1Beta2Reason = clusterv1beta1.ReadyV1Beta2Reason + + // KubeadmConfigNotReadyV1Beta2Reason surfaces when the KubeadmConfig is not ready. + KubeadmConfigNotReadyV1Beta2Reason = clusterv1beta1.NotReadyV1Beta2Reason + + // KubeadmConfigReadyUnknownV1Beta2Reason surfaces when KubeadmConfig readiness is unknown. + KubeadmConfigReadyUnknownV1Beta2Reason = clusterv1beta1.ReadyUnknownV1Beta2Reason +) + +// KubeadmConfig's CertificatesAvailable condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmConfigCertificatesAvailableV1Beta2Condition documents that cluster certificates required + // for generating the bootstrap data secret are available. + KubeadmConfigCertificatesAvailableV1Beta2Condition = "CertificatesAvailable" + + // KubeadmConfigCertificatesAvailableV1Beta2Reason surfaces when certificates required for machine bootstrap are is available. + KubeadmConfigCertificatesAvailableV1Beta2Reason = clusterv1beta1.AvailableV1Beta2Reason + + // KubeadmConfigCertificatesAvailableInternalErrorV1Beta2Reason surfaces unexpected failures when reading or + // generating certificates required for machine bootstrap. + KubeadmConfigCertificatesAvailableInternalErrorV1Beta2Reason = clusterv1beta1.InternalErrorV1Beta2Reason +) + +// KubeadmConfig's DataSecretAvailable condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmConfigDataSecretAvailableV1Beta2Condition is true if the bootstrap secret is available. + KubeadmConfigDataSecretAvailableV1Beta2Condition = "DataSecretAvailable" + + // KubeadmConfigDataSecretAvailableV1Beta2Reason surfaces when the bootstrap secret is available. + KubeadmConfigDataSecretAvailableV1Beta2Reason = clusterv1beta1.AvailableV1Beta2Reason + + // KubeadmConfigDataSecretNotAvailableV1Beta2Reason surfaces when the bootstrap secret is not available. + KubeadmConfigDataSecretNotAvailableV1Beta2Reason = clusterv1beta1.NotAvailableV1Beta2Reason +) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/zz_generated.conversion.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/zz_generated.conversion.go new file mode 100644 index 0000000000..16b20da9ed --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/zz_generated.conversion.go @@ -0,0 +1,1814 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by conversion-gen. DO NOT EDIT. + +package v1beta1 + +import ( + unsafe "unsafe" + + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + v1beta2 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" + corev1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" + corev1beta2 "sigs.k8s.io/cluster-api/api/core/v1beta2" +) + +func init() { + localSchemeBuilder.Register(RegisterConversions) +} + +// RegisterConversions adds conversion functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*APIEndpoint)(nil), (*v1beta2.APIEndpoint)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(a.(*APIEndpoint), b.(*v1beta2.APIEndpoint), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.APIEndpoint)(nil), (*APIEndpoint)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint(a.(*v1beta2.APIEndpoint), b.(*APIEndpoint), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*BootstrapTokenDiscovery)(nil), (*v1beta2.BootstrapTokenDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_BootstrapTokenDiscovery_To_v1beta2_BootstrapTokenDiscovery(a.(*BootstrapTokenDiscovery), b.(*v1beta2.BootstrapTokenDiscovery), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.BootstrapTokenDiscovery)(nil), (*BootstrapTokenDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_BootstrapTokenDiscovery_To_v1beta1_BootstrapTokenDiscovery(a.(*v1beta2.BootstrapTokenDiscovery), b.(*BootstrapTokenDiscovery), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*BootstrapTokenString)(nil), (*v1beta2.BootstrapTokenString)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_BootstrapTokenString_To_v1beta2_BootstrapTokenString(a.(*BootstrapTokenString), b.(*v1beta2.BootstrapTokenString), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.BootstrapTokenString)(nil), (*BootstrapTokenString)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_BootstrapTokenString_To_v1beta1_BootstrapTokenString(a.(*v1beta2.BootstrapTokenString), b.(*BootstrapTokenString), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ContainerLinuxConfig)(nil), (*v1beta2.ContainerLinuxConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ContainerLinuxConfig_To_v1beta2_ContainerLinuxConfig(a.(*ContainerLinuxConfig), b.(*v1beta2.ContainerLinuxConfig), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.ContainerLinuxConfig)(nil), (*ContainerLinuxConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_ContainerLinuxConfig_To_v1beta1_ContainerLinuxConfig(a.(*v1beta2.ContainerLinuxConfig), b.(*ContainerLinuxConfig), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*DiskSetup)(nil), (*v1beta2.DiskSetup)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_DiskSetup_To_v1beta2_DiskSetup(a.(*DiskSetup), b.(*v1beta2.DiskSetup), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.DiskSetup)(nil), (*DiskSetup)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_DiskSetup_To_v1beta1_DiskSetup(a.(*v1beta2.DiskSetup), b.(*DiskSetup), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*EnvVar)(nil), (*v1beta2.EnvVar)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_EnvVar_To_v1beta2_EnvVar(a.(*EnvVar), b.(*v1beta2.EnvVar), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.EnvVar)(nil), (*EnvVar)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_EnvVar_To_v1beta1_EnvVar(a.(*v1beta2.EnvVar), b.(*EnvVar), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ExternalEtcd)(nil), (*v1beta2.ExternalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ExternalEtcd_To_v1beta2_ExternalEtcd(a.(*ExternalEtcd), b.(*v1beta2.ExternalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.ExternalEtcd)(nil), (*ExternalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_ExternalEtcd_To_v1beta1_ExternalEtcd(a.(*v1beta2.ExternalEtcd), b.(*ExternalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*FileSource)(nil), (*v1beta2.FileSource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_FileSource_To_v1beta2_FileSource(a.(*FileSource), b.(*v1beta2.FileSource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.FileSource)(nil), (*FileSource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_FileSource_To_v1beta1_FileSource(a.(*v1beta2.FileSource), b.(*FileSource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*Filesystem)(nil), (*v1beta2.Filesystem)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Filesystem_To_v1beta2_Filesystem(a.(*Filesystem), b.(*v1beta2.Filesystem), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.Filesystem)(nil), (*Filesystem)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_Filesystem_To_v1beta1_Filesystem(a.(*v1beta2.Filesystem), b.(*Filesystem), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*HostPathMount)(nil), (*v1beta2.HostPathMount)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_HostPathMount_To_v1beta2_HostPathMount(a.(*HostPathMount), b.(*v1beta2.HostPathMount), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.HostPathMount)(nil), (*HostPathMount)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_HostPathMount_To_v1beta1_HostPathMount(a.(*v1beta2.HostPathMount), b.(*HostPathMount), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*JoinControlPlane)(nil), (*v1beta2.JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_JoinControlPlane_To_v1beta2_JoinControlPlane(a.(*JoinControlPlane), b.(*v1beta2.JoinControlPlane), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.JoinControlPlane)(nil), (*JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_JoinControlPlane_To_v1beta1_JoinControlPlane(a.(*v1beta2.JoinControlPlane), b.(*JoinControlPlane), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeConfigAuthExec)(nil), (*v1beta2.KubeConfigAuthExec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeConfigAuthExec_To_v1beta2_KubeConfigAuthExec(a.(*KubeConfigAuthExec), b.(*v1beta2.KubeConfigAuthExec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeConfigAuthExec)(nil), (*KubeConfigAuthExec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeConfigAuthExec_To_v1beta1_KubeConfigAuthExec(a.(*v1beta2.KubeConfigAuthExec), b.(*KubeConfigAuthExec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeConfigAuthExecEnv)(nil), (*v1beta2.KubeConfigAuthExecEnv)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeConfigAuthExecEnv_To_v1beta2_KubeConfigAuthExecEnv(a.(*KubeConfigAuthExecEnv), b.(*v1beta2.KubeConfigAuthExecEnv), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeConfigAuthExecEnv)(nil), (*KubeConfigAuthExecEnv)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeConfigAuthExecEnv_To_v1beta1_KubeConfigAuthExecEnv(a.(*v1beta2.KubeConfigAuthExecEnv), b.(*KubeConfigAuthExecEnv), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeConfigAuthProvider)(nil), (*v1beta2.KubeConfigAuthProvider)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeConfigAuthProvider_To_v1beta2_KubeConfigAuthProvider(a.(*KubeConfigAuthProvider), b.(*v1beta2.KubeConfigAuthProvider), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeConfigAuthProvider)(nil), (*KubeConfigAuthProvider)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeConfigAuthProvider_To_v1beta1_KubeConfigAuthProvider(a.(*v1beta2.KubeConfigAuthProvider), b.(*KubeConfigAuthProvider), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeConfigCluster)(nil), (*v1beta2.KubeConfigCluster)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeConfigCluster_To_v1beta2_KubeConfigCluster(a.(*KubeConfigCluster), b.(*v1beta2.KubeConfigCluster), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeConfigCluster)(nil), (*KubeConfigCluster)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeConfigCluster_To_v1beta1_KubeConfigCluster(a.(*v1beta2.KubeConfigCluster), b.(*KubeConfigCluster), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeadmConfig)(nil), (*v1beta2.KubeadmConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmConfig_To_v1beta2_KubeadmConfig(a.(*KubeadmConfig), b.(*v1beta2.KubeadmConfig), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeadmConfig)(nil), (*KubeadmConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmConfig_To_v1beta1_KubeadmConfig(a.(*v1beta2.KubeadmConfig), b.(*KubeadmConfig), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeadmConfigList)(nil), (*v1beta2.KubeadmConfigList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmConfigList_To_v1beta2_KubeadmConfigList(a.(*KubeadmConfigList), b.(*v1beta2.KubeadmConfigList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeadmConfigList)(nil), (*KubeadmConfigList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmConfigList_To_v1beta1_KubeadmConfigList(a.(*v1beta2.KubeadmConfigList), b.(*KubeadmConfigList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeadmConfigTemplate)(nil), (*v1beta2.KubeadmConfigTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmConfigTemplate_To_v1beta2_KubeadmConfigTemplate(a.(*KubeadmConfigTemplate), b.(*v1beta2.KubeadmConfigTemplate), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeadmConfigTemplate)(nil), (*KubeadmConfigTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmConfigTemplate_To_v1beta1_KubeadmConfigTemplate(a.(*v1beta2.KubeadmConfigTemplate), b.(*KubeadmConfigTemplate), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeadmConfigTemplateList)(nil), (*v1beta2.KubeadmConfigTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmConfigTemplateList_To_v1beta2_KubeadmConfigTemplateList(a.(*KubeadmConfigTemplateList), b.(*v1beta2.KubeadmConfigTemplateList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeadmConfigTemplateList)(nil), (*KubeadmConfigTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmConfigTemplateList_To_v1beta1_KubeadmConfigTemplateList(a.(*v1beta2.KubeadmConfigTemplateList), b.(*KubeadmConfigTemplateList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeadmConfigTemplateResource)(nil), (*v1beta2.KubeadmConfigTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmConfigTemplateResource_To_v1beta2_KubeadmConfigTemplateResource(a.(*KubeadmConfigTemplateResource), b.(*v1beta2.KubeadmConfigTemplateResource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeadmConfigTemplateResource)(nil), (*KubeadmConfigTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmConfigTemplateResource_To_v1beta1_KubeadmConfigTemplateResource(a.(*v1beta2.KubeadmConfigTemplateResource), b.(*KubeadmConfigTemplateResource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeadmConfigTemplateSpec)(nil), (*v1beta2.KubeadmConfigTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmConfigTemplateSpec_To_v1beta2_KubeadmConfigTemplateSpec(a.(*KubeadmConfigTemplateSpec), b.(*v1beta2.KubeadmConfigTemplateSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeadmConfigTemplateSpec)(nil), (*KubeadmConfigTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmConfigTemplateSpec_To_v1beta1_KubeadmConfigTemplateSpec(a.(*v1beta2.KubeadmConfigTemplateSpec), b.(*KubeadmConfigTemplateSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*NTP)(nil), (*v1beta2.NTP)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_NTP_To_v1beta2_NTP(a.(*NTP), b.(*v1beta2.NTP), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.NTP)(nil), (*NTP)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_NTP_To_v1beta1_NTP(a.(*v1beta2.NTP), b.(*NTP), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*Partition)(nil), (*v1beta2.Partition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Partition_To_v1beta2_Partition(a.(*Partition), b.(*v1beta2.Partition), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.Partition)(nil), (*Partition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_Partition_To_v1beta1_Partition(a.(*v1beta2.Partition), b.(*Partition), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*PasswdSource)(nil), (*v1beta2.PasswdSource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_PasswdSource_To_v1beta2_PasswdSource(a.(*PasswdSource), b.(*v1beta2.PasswdSource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.PasswdSource)(nil), (*PasswdSource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_PasswdSource_To_v1beta1_PasswdSource(a.(*v1beta2.PasswdSource), b.(*PasswdSource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*Patches)(nil), (*v1beta2.Patches)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Patches_To_v1beta2_Patches(a.(*Patches), b.(*v1beta2.Patches), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.Patches)(nil), (*Patches)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_Patches_To_v1beta1_Patches(a.(*v1beta2.Patches), b.(*Patches), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*SecretFileSource)(nil), (*v1beta2.SecretFileSource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_SecretFileSource_To_v1beta2_SecretFileSource(a.(*SecretFileSource), b.(*v1beta2.SecretFileSource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.SecretFileSource)(nil), (*SecretFileSource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_SecretFileSource_To_v1beta1_SecretFileSource(a.(*v1beta2.SecretFileSource), b.(*SecretFileSource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*SecretPasswdSource)(nil), (*v1beta2.SecretPasswdSource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_SecretPasswdSource_To_v1beta2_SecretPasswdSource(a.(*SecretPasswdSource), b.(*v1beta2.SecretPasswdSource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.SecretPasswdSource)(nil), (*SecretPasswdSource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_SecretPasswdSource_To_v1beta1_SecretPasswdSource(a.(*v1beta2.SecretPasswdSource), b.(*SecretPasswdSource), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1.Condition)(nil), (*corev1beta1.Condition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_Condition_To_v1beta1_Condition(a.(*v1.Condition), b.(*corev1beta1.Condition), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*APIServer)(nil), (*v1beta2.APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_APIServer_To_v1beta2_APIServer(a.(*APIServer), b.(*v1beta2.APIServer), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*BootstrapToken)(nil), (*v1beta2.BootstrapToken)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_BootstrapToken_To_v1beta2_BootstrapToken(a.(*BootstrapToken), b.(*v1beta2.BootstrapToken), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ClusterConfiguration)(nil), (*v1beta2.ClusterConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ClusterConfiguration_To_v1beta2_ClusterConfiguration(a.(*ClusterConfiguration), b.(*v1beta2.ClusterConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*corev1beta1.Condition)(nil), (*v1.Condition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Condition_To_v1_Condition(a.(*corev1beta1.Condition), b.(*v1.Condition), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControllerManager)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ControlPlaneComponent_To_v1beta2_ControllerManager(a.(*ControlPlaneComponent), b.(*v1beta2.ControllerManager), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.Scheduler)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ControlPlaneComponent_To_v1beta2_Scheduler(a.(*ControlPlaneComponent), b.(*v1beta2.Scheduler), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*DNS)(nil), (*v1beta2.DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_DNS_To_v1beta2_DNS(a.(*DNS), b.(*v1beta2.DNS), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*Discovery)(nil), (*v1beta2.Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Discovery_To_v1beta2_Discovery(a.(*Discovery), b.(*v1beta2.Discovery), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*Etcd)(nil), (*v1beta2.Etcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Etcd_To_v1beta2_Etcd(a.(*Etcd), b.(*v1beta2.Etcd), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*FileDiscoveryKubeConfig)(nil), (*v1beta2.FileDiscoveryKubeConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_FileDiscoveryKubeConfig_To_v1beta2_FileDiscoveryKubeConfig(a.(*FileDiscoveryKubeConfig), b.(*v1beta2.FileDiscoveryKubeConfig), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*FileDiscovery)(nil), (*v1beta2.FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_FileDiscovery_To_v1beta2_FileDiscovery(a.(*FileDiscovery), b.(*v1beta2.FileDiscovery), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*File)(nil), (*v1beta2.File)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_File_To_v1beta2_File(a.(*File), b.(*v1beta2.File), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*IgnitionSpec)(nil), (*v1beta2.IgnitionSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_IgnitionSpec_To_v1beta2_IgnitionSpec(a.(*IgnitionSpec), b.(*v1beta2.IgnitionSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*InitConfiguration)(nil), (*v1beta2.InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_InitConfiguration_To_v1beta2_InitConfiguration(a.(*InitConfiguration), b.(*v1beta2.InitConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*JoinConfiguration)(nil), (*v1beta2.JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(a.(*JoinConfiguration), b.(*v1beta2.JoinConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*KubeConfigUser)(nil), (*v1beta2.KubeConfigUser)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeConfigUser_To_v1beta2_KubeConfigUser(a.(*KubeConfigUser), b.(*v1beta2.KubeConfigUser), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*KubeadmConfigSpec)(nil), (*v1beta2.KubeadmConfigSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(a.(*KubeadmConfigSpec), b.(*v1beta2.KubeadmConfigSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*KubeadmConfigStatus)(nil), (*v1beta2.KubeadmConfigStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmConfigStatus_To_v1beta2_KubeadmConfigStatus(a.(*KubeadmConfigStatus), b.(*v1beta2.KubeadmConfigStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*corev1beta1.ObjectMeta)(nil), (*corev1beta2.ObjectMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ObjectMeta_To_v1beta2_ObjectMeta(a.(*corev1beta1.ObjectMeta), b.(*corev1beta2.ObjectMeta), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*User)(nil), (*v1beta2.User)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_User_To_v1beta2_User(a.(*User), b.(*v1beta2.User), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.APIServer)(nil), (*APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_APIServer_To_v1beta1_APIServer(a.(*v1beta2.APIServer), b.(*APIServer), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.BootstrapToken)(nil), (*BootstrapToken)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_BootstrapToken_To_v1beta1_BootstrapToken(a.(*v1beta2.BootstrapToken), b.(*BootstrapToken), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.ClusterConfiguration)(nil), (*ClusterConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_ClusterConfiguration_To_v1beta1_ClusterConfiguration(a.(*v1beta2.ClusterConfiguration), b.(*ClusterConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.ControllerManager)(nil), (*ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_ControllerManager_To_v1beta1_ControlPlaneComponent(a.(*v1beta2.ControllerManager), b.(*ControlPlaneComponent), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.DNS)(nil), (*DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_DNS_To_v1beta1_DNS(a.(*v1beta2.DNS), b.(*DNS), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.Discovery)(nil), (*Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_Discovery_To_v1beta1_Discovery(a.(*v1beta2.Discovery), b.(*Discovery), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.Etcd)(nil), (*Etcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_Etcd_To_v1beta1_Etcd(a.(*v1beta2.Etcd), b.(*Etcd), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.FileDiscoveryKubeConfig)(nil), (*FileDiscoveryKubeConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_FileDiscoveryKubeConfig_To_v1beta1_FileDiscoveryKubeConfig(a.(*v1beta2.FileDiscoveryKubeConfig), b.(*FileDiscoveryKubeConfig), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.FileDiscovery)(nil), (*FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_FileDiscovery_To_v1beta1_FileDiscovery(a.(*v1beta2.FileDiscovery), b.(*FileDiscovery), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.File)(nil), (*File)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_File_To_v1beta1_File(a.(*v1beta2.File), b.(*File), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.IgnitionSpec)(nil), (*IgnitionSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_IgnitionSpec_To_v1beta1_IgnitionSpec(a.(*v1beta2.IgnitionSpec), b.(*IgnitionSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.InitConfiguration)(nil), (*InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(a.(*v1beta2.InitConfiguration), b.(*InitConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(a.(*v1beta2.JoinConfiguration), b.(*JoinConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.KubeConfigUser)(nil), (*KubeConfigUser)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeConfigUser_To_v1beta1_KubeConfigUser(a.(*v1beta2.KubeConfigUser), b.(*KubeConfigUser), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.KubeadmConfigSpec)(nil), (*KubeadmConfigSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmConfigSpec_To_v1beta1_KubeadmConfigSpec(a.(*v1beta2.KubeadmConfigSpec), b.(*KubeadmConfigSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.KubeadmConfigStatus)(nil), (*KubeadmConfigStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmConfigStatus_To_v1beta1_KubeadmConfigStatus(a.(*v1beta2.KubeadmConfigStatus), b.(*KubeadmConfigStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.LocalEtcd)(nil), (*LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(a.(*v1beta2.LocalEtcd), b.(*LocalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.NodeRegistrationOptions)(nil), (*NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(a.(*v1beta2.NodeRegistrationOptions), b.(*NodeRegistrationOptions), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*corev1beta2.ObjectMeta)(nil), (*corev1beta1.ObjectMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_ObjectMeta_To_v1beta1_ObjectMeta(a.(*corev1beta2.ObjectMeta), b.(*corev1beta1.ObjectMeta), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.Scheduler)(nil), (*ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_Scheduler_To_v1beta1_ControlPlaneComponent(a.(*v1beta2.Scheduler), b.(*ControlPlaneComponent), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.User)(nil), (*User)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_User_To_v1beta1_User(a.(*v1beta2.User), b.(*User), scope) + }); err != nil { + return err + } + return nil +} + +func autoConvert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(in *APIEndpoint, out *v1beta2.APIEndpoint, s conversion.Scope) error { + out.AdvertiseAddress = in.AdvertiseAddress + out.BindPort = in.BindPort + return nil +} + +// Convert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint is an autogenerated conversion function. +func Convert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(in *APIEndpoint, out *v1beta2.APIEndpoint, s conversion.Scope) error { + return autoConvert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(in, out, s) +} + +func autoConvert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint(in *v1beta2.APIEndpoint, out *APIEndpoint, s conversion.Scope) error { + out.AdvertiseAddress = in.AdvertiseAddress + out.BindPort = in.BindPort + return nil +} + +// Convert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint is an autogenerated conversion function. +func Convert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint(in *v1beta2.APIEndpoint, out *APIEndpoint, s conversion.Scope) error { + return autoConvert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint(in, out, s) +} + +func autoConvert_v1beta1_APIServer_To_v1beta2_APIServer(in *APIServer, out *v1beta2.APIServer, s conversion.Scope) error { + // WARNING: in.ControlPlaneComponent requires manual conversion: does not exist in peer-type + out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) + // WARNING: in.TimeoutForControlPlane requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_APIServer_To_v1beta1_APIServer(in *v1beta2.APIServer, out *APIServer, s conversion.Scope) error { + // WARNING: in.ExtraArgs requires manual conversion: does not exist in peer-type + // WARNING: in.ExtraVolumes requires manual conversion: does not exist in peer-type + // WARNING: in.ExtraEnvs requires manual conversion: does not exist in peer-type + out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) + return nil +} + +func autoConvert_v1beta1_BootstrapToken_To_v1beta2_BootstrapToken(in *BootstrapToken, out *v1beta2.BootstrapToken, s conversion.Scope) error { + // WARNING: in.Token requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.BootstrapTokenString vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.BootstrapTokenString) + out.Description = in.Description + // WARNING: in.TTL requires manual conversion: does not exist in peer-type + // WARNING: in.Expires requires manual conversion: inconvertible types (*k8s.io/apimachinery/pkg/apis/meta/v1.Time vs k8s.io/apimachinery/pkg/apis/meta/v1.Time) + out.Usages = *(*[]string)(unsafe.Pointer(&in.Usages)) + out.Groups = *(*[]string)(unsafe.Pointer(&in.Groups)) + return nil +} + +func autoConvert_v1beta2_BootstrapToken_To_v1beta1_BootstrapToken(in *v1beta2.BootstrapToken, out *BootstrapToken, s conversion.Scope) error { + // WARNING: in.Token requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.BootstrapTokenString vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.BootstrapTokenString) + out.Description = in.Description + // WARNING: in.TTLSeconds requires manual conversion: does not exist in peer-type + // WARNING: in.Expires requires manual conversion: inconvertible types (k8s.io/apimachinery/pkg/apis/meta/v1.Time vs *k8s.io/apimachinery/pkg/apis/meta/v1.Time) + out.Usages = *(*[]string)(unsafe.Pointer(&in.Usages)) + out.Groups = *(*[]string)(unsafe.Pointer(&in.Groups)) + return nil +} + +func autoConvert_v1beta1_BootstrapTokenDiscovery_To_v1beta2_BootstrapTokenDiscovery(in *BootstrapTokenDiscovery, out *v1beta2.BootstrapTokenDiscovery, s conversion.Scope) error { + out.Token = in.Token + out.APIServerEndpoint = in.APIServerEndpoint + out.CACertHashes = *(*[]string)(unsafe.Pointer(&in.CACertHashes)) + if err := v1.Convert_bool_To_Pointer_bool(&in.UnsafeSkipCAVerification, &out.UnsafeSkipCAVerification, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_BootstrapTokenDiscovery_To_v1beta2_BootstrapTokenDiscovery is an autogenerated conversion function. +func Convert_v1beta1_BootstrapTokenDiscovery_To_v1beta2_BootstrapTokenDiscovery(in *BootstrapTokenDiscovery, out *v1beta2.BootstrapTokenDiscovery, s conversion.Scope) error { + return autoConvert_v1beta1_BootstrapTokenDiscovery_To_v1beta2_BootstrapTokenDiscovery(in, out, s) +} + +func autoConvert_v1beta2_BootstrapTokenDiscovery_To_v1beta1_BootstrapTokenDiscovery(in *v1beta2.BootstrapTokenDiscovery, out *BootstrapTokenDiscovery, s conversion.Scope) error { + out.Token = in.Token + out.APIServerEndpoint = in.APIServerEndpoint + out.CACertHashes = *(*[]string)(unsafe.Pointer(&in.CACertHashes)) + if err := v1.Convert_Pointer_bool_To_bool(&in.UnsafeSkipCAVerification, &out.UnsafeSkipCAVerification, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_BootstrapTokenDiscovery_To_v1beta1_BootstrapTokenDiscovery is an autogenerated conversion function. +func Convert_v1beta2_BootstrapTokenDiscovery_To_v1beta1_BootstrapTokenDiscovery(in *v1beta2.BootstrapTokenDiscovery, out *BootstrapTokenDiscovery, s conversion.Scope) error { + return autoConvert_v1beta2_BootstrapTokenDiscovery_To_v1beta1_BootstrapTokenDiscovery(in, out, s) +} + +func autoConvert_v1beta1_BootstrapTokenString_To_v1beta2_BootstrapTokenString(in *BootstrapTokenString, out *v1beta2.BootstrapTokenString, s conversion.Scope) error { + out.ID = in.ID + out.Secret = in.Secret + return nil +} + +// Convert_v1beta1_BootstrapTokenString_To_v1beta2_BootstrapTokenString is an autogenerated conversion function. +func Convert_v1beta1_BootstrapTokenString_To_v1beta2_BootstrapTokenString(in *BootstrapTokenString, out *v1beta2.BootstrapTokenString, s conversion.Scope) error { + return autoConvert_v1beta1_BootstrapTokenString_To_v1beta2_BootstrapTokenString(in, out, s) +} + +func autoConvert_v1beta2_BootstrapTokenString_To_v1beta1_BootstrapTokenString(in *v1beta2.BootstrapTokenString, out *BootstrapTokenString, s conversion.Scope) error { + out.ID = in.ID + out.Secret = in.Secret + return nil +} + +// Convert_v1beta2_BootstrapTokenString_To_v1beta1_BootstrapTokenString is an autogenerated conversion function. +func Convert_v1beta2_BootstrapTokenString_To_v1beta1_BootstrapTokenString(in *v1beta2.BootstrapTokenString, out *BootstrapTokenString, s conversion.Scope) error { + return autoConvert_v1beta2_BootstrapTokenString_To_v1beta1_BootstrapTokenString(in, out, s) +} + +func autoConvert_v1beta1_ClusterConfiguration_To_v1beta2_ClusterConfiguration(in *ClusterConfiguration, out *v1beta2.ClusterConfiguration, s conversion.Scope) error { + // WARNING: in.TypeMeta requires manual conversion: does not exist in peer-type + if err := Convert_v1beta1_Etcd_To_v1beta2_Etcd(&in.Etcd, &out.Etcd, s); err != nil { + return err + } + // WARNING: in.Networking requires manual conversion: does not exist in peer-type + // WARNING: in.KubernetesVersion requires manual conversion: does not exist in peer-type + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + if err := Convert_v1beta1_APIServer_To_v1beta2_APIServer(&in.APIServer, &out.APIServer, s); err != nil { + return err + } + if err := Convert_v1beta1_ControlPlaneComponent_To_v1beta2_ControllerManager(&in.ControllerManager, &out.ControllerManager, s); err != nil { + return err + } + if err := Convert_v1beta1_ControlPlaneComponent_To_v1beta2_Scheduler(&in.Scheduler, &out.Scheduler, s); err != nil { + return err + } + if err := Convert_v1beta1_DNS_To_v1beta2_DNS(&in.DNS, &out.DNS, s); err != nil { + return err + } + out.CertificatesDir = in.CertificatesDir + out.ImageRepository = in.ImageRepository + out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates)) + // WARNING: in.ClusterName requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_ClusterConfiguration_To_v1beta1_ClusterConfiguration(in *v1beta2.ClusterConfiguration, out *ClusterConfiguration, s conversion.Scope) error { + if err := Convert_v1beta2_Etcd_To_v1beta1_Etcd(&in.Etcd, &out.Etcd, s); err != nil { + return err + } + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + if err := Convert_v1beta2_APIServer_To_v1beta1_APIServer(&in.APIServer, &out.APIServer, s); err != nil { + return err + } + if err := Convert_v1beta2_ControllerManager_To_v1beta1_ControlPlaneComponent(&in.ControllerManager, &out.ControllerManager, s); err != nil { + return err + } + if err := Convert_v1beta2_Scheduler_To_v1beta1_ControlPlaneComponent(&in.Scheduler, &out.Scheduler, s); err != nil { + return err + } + if err := Convert_v1beta2_DNS_To_v1beta1_DNS(&in.DNS, &out.DNS, s); err != nil { + return err + } + out.CertificatesDir = in.CertificatesDir + out.ImageRepository = in.ImageRepository + out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates)) + // WARNING: in.CertificateValidityPeriodDays requires manual conversion: does not exist in peer-type + // WARNING: in.CACertificateValidityPeriodDays requires manual conversion: does not exist in peer-type + // WARNING: in.EncryptionAlgorithm requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_ContainerLinuxConfig_To_v1beta2_ContainerLinuxConfig(in *ContainerLinuxConfig, out *v1beta2.ContainerLinuxConfig, s conversion.Scope) error { + out.AdditionalConfig = in.AdditionalConfig + if err := v1.Convert_bool_To_Pointer_bool(&in.Strict, &out.Strict, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_ContainerLinuxConfig_To_v1beta2_ContainerLinuxConfig is an autogenerated conversion function. +func Convert_v1beta1_ContainerLinuxConfig_To_v1beta2_ContainerLinuxConfig(in *ContainerLinuxConfig, out *v1beta2.ContainerLinuxConfig, s conversion.Scope) error { + return autoConvert_v1beta1_ContainerLinuxConfig_To_v1beta2_ContainerLinuxConfig(in, out, s) +} + +func autoConvert_v1beta2_ContainerLinuxConfig_To_v1beta1_ContainerLinuxConfig(in *v1beta2.ContainerLinuxConfig, out *ContainerLinuxConfig, s conversion.Scope) error { + out.AdditionalConfig = in.AdditionalConfig + if err := v1.Convert_Pointer_bool_To_bool(&in.Strict, &out.Strict, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_ContainerLinuxConfig_To_v1beta1_ContainerLinuxConfig is an autogenerated conversion function. +func Convert_v1beta2_ContainerLinuxConfig_To_v1beta1_ContainerLinuxConfig(in *v1beta2.ContainerLinuxConfig, out *ContainerLinuxConfig, s conversion.Scope) error { + return autoConvert_v1beta2_ContainerLinuxConfig_To_v1beta1_ContainerLinuxConfig(in, out, s) +} + +func autoConvert_v1beta1_DNS_To_v1beta2_DNS(in *DNS, out *v1beta2.DNS, s conversion.Scope) error { + // WARNING: in.ImageMeta requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_DNS_To_v1beta1_DNS(in *v1beta2.DNS, out *DNS, s conversion.Scope) error { + // WARNING: in.ImageRepository requires manual conversion: does not exist in peer-type + // WARNING: in.ImageTag requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_Discovery_To_v1beta2_Discovery(in *Discovery, out *v1beta2.Discovery, s conversion.Scope) error { + // WARNING: in.BootstrapToken requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.BootstrapTokenDiscovery vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.BootstrapTokenDiscovery) + // WARNING: in.File requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.FileDiscovery vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.FileDiscovery) + out.TLSBootstrapToken = in.TLSBootstrapToken + // WARNING: in.Timeout requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_Discovery_To_v1beta1_Discovery(in *v1beta2.Discovery, out *Discovery, s conversion.Scope) error { + // WARNING: in.BootstrapToken requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.BootstrapTokenDiscovery vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.BootstrapTokenDiscovery) + // WARNING: in.File requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.FileDiscovery vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.FileDiscovery) + out.TLSBootstrapToken = in.TLSBootstrapToken + return nil +} + +func autoConvert_v1beta1_DiskSetup_To_v1beta2_DiskSetup(in *DiskSetup, out *v1beta2.DiskSetup, s conversion.Scope) error { + if in.Partitions != nil { + in, out := &in.Partitions, &out.Partitions + *out = make([]v1beta2.Partition, len(*in)) + for i := range *in { + if err := Convert_v1beta1_Partition_To_v1beta2_Partition(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Partitions = nil + } + if in.Filesystems != nil { + in, out := &in.Filesystems, &out.Filesystems + *out = make([]v1beta2.Filesystem, len(*in)) + for i := range *in { + if err := Convert_v1beta1_Filesystem_To_v1beta2_Filesystem(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Filesystems = nil + } + return nil +} + +// Convert_v1beta1_DiskSetup_To_v1beta2_DiskSetup is an autogenerated conversion function. +func Convert_v1beta1_DiskSetup_To_v1beta2_DiskSetup(in *DiskSetup, out *v1beta2.DiskSetup, s conversion.Scope) error { + return autoConvert_v1beta1_DiskSetup_To_v1beta2_DiskSetup(in, out, s) +} + +func autoConvert_v1beta2_DiskSetup_To_v1beta1_DiskSetup(in *v1beta2.DiskSetup, out *DiskSetup, s conversion.Scope) error { + if in.Partitions != nil { + in, out := &in.Partitions, &out.Partitions + *out = make([]Partition, len(*in)) + for i := range *in { + if err := Convert_v1beta2_Partition_To_v1beta1_Partition(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Partitions = nil + } + if in.Filesystems != nil { + in, out := &in.Filesystems, &out.Filesystems + *out = make([]Filesystem, len(*in)) + for i := range *in { + if err := Convert_v1beta2_Filesystem_To_v1beta1_Filesystem(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Filesystems = nil + } + return nil +} + +// Convert_v1beta2_DiskSetup_To_v1beta1_DiskSetup is an autogenerated conversion function. +func Convert_v1beta2_DiskSetup_To_v1beta1_DiskSetup(in *v1beta2.DiskSetup, out *DiskSetup, s conversion.Scope) error { + return autoConvert_v1beta2_DiskSetup_To_v1beta1_DiskSetup(in, out, s) +} + +func autoConvert_v1beta1_EnvVar_To_v1beta2_EnvVar(in *EnvVar, out *v1beta2.EnvVar, s conversion.Scope) error { + out.EnvVar = in.EnvVar + return nil +} + +// Convert_v1beta1_EnvVar_To_v1beta2_EnvVar is an autogenerated conversion function. +func Convert_v1beta1_EnvVar_To_v1beta2_EnvVar(in *EnvVar, out *v1beta2.EnvVar, s conversion.Scope) error { + return autoConvert_v1beta1_EnvVar_To_v1beta2_EnvVar(in, out, s) +} + +func autoConvert_v1beta2_EnvVar_To_v1beta1_EnvVar(in *v1beta2.EnvVar, out *EnvVar, s conversion.Scope) error { + out.EnvVar = in.EnvVar + return nil +} + +// Convert_v1beta2_EnvVar_To_v1beta1_EnvVar is an autogenerated conversion function. +func Convert_v1beta2_EnvVar_To_v1beta1_EnvVar(in *v1beta2.EnvVar, out *EnvVar, s conversion.Scope) error { + return autoConvert_v1beta2_EnvVar_To_v1beta1_EnvVar(in, out, s) +} + +func autoConvert_v1beta1_Etcd_To_v1beta2_Etcd(in *Etcd, out *v1beta2.Etcd, s conversion.Scope) error { + // WARNING: in.Local requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.LocalEtcd vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.LocalEtcd) + // WARNING: in.External requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.ExternalEtcd vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.ExternalEtcd) + return nil +} + +func autoConvert_v1beta2_Etcd_To_v1beta1_Etcd(in *v1beta2.Etcd, out *Etcd, s conversion.Scope) error { + // WARNING: in.Local requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.LocalEtcd vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.LocalEtcd) + // WARNING: in.External requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.ExternalEtcd vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.ExternalEtcd) + return nil +} + +func autoConvert_v1beta1_ExternalEtcd_To_v1beta2_ExternalEtcd(in *ExternalEtcd, out *v1beta2.ExternalEtcd, s conversion.Scope) error { + out.Endpoints = *(*[]string)(unsafe.Pointer(&in.Endpoints)) + out.CAFile = in.CAFile + out.CertFile = in.CertFile + out.KeyFile = in.KeyFile + return nil +} + +// Convert_v1beta1_ExternalEtcd_To_v1beta2_ExternalEtcd is an autogenerated conversion function. +func Convert_v1beta1_ExternalEtcd_To_v1beta2_ExternalEtcd(in *ExternalEtcd, out *v1beta2.ExternalEtcd, s conversion.Scope) error { + return autoConvert_v1beta1_ExternalEtcd_To_v1beta2_ExternalEtcd(in, out, s) +} + +func autoConvert_v1beta2_ExternalEtcd_To_v1beta1_ExternalEtcd(in *v1beta2.ExternalEtcd, out *ExternalEtcd, s conversion.Scope) error { + out.Endpoints = *(*[]string)(unsafe.Pointer(&in.Endpoints)) + out.CAFile = in.CAFile + out.CertFile = in.CertFile + out.KeyFile = in.KeyFile + return nil +} + +// Convert_v1beta2_ExternalEtcd_To_v1beta1_ExternalEtcd is an autogenerated conversion function. +func Convert_v1beta2_ExternalEtcd_To_v1beta1_ExternalEtcd(in *v1beta2.ExternalEtcd, out *ExternalEtcd, s conversion.Scope) error { + return autoConvert_v1beta2_ExternalEtcd_To_v1beta1_ExternalEtcd(in, out, s) +} + +func autoConvert_v1beta1_File_To_v1beta2_File(in *File, out *v1beta2.File, s conversion.Scope) error { + out.Path = in.Path + out.Owner = in.Owner + out.Permissions = in.Permissions + out.Encoding = v1beta2.Encoding(in.Encoding) + if err := v1.Convert_bool_To_Pointer_bool(&in.Append, &out.Append, s); err != nil { + return err + } + out.Content = in.Content + // WARNING: in.ContentFrom requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.FileSource vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.FileSource) + return nil +} + +func autoConvert_v1beta2_File_To_v1beta1_File(in *v1beta2.File, out *File, s conversion.Scope) error { + out.Path = in.Path + out.Owner = in.Owner + out.Permissions = in.Permissions + out.Encoding = Encoding(in.Encoding) + if err := v1.Convert_Pointer_bool_To_bool(&in.Append, &out.Append, s); err != nil { + return err + } + out.Content = in.Content + // WARNING: in.ContentFrom requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.FileSource vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.FileSource) + return nil +} + +func autoConvert_v1beta1_FileDiscovery_To_v1beta2_FileDiscovery(in *FileDiscovery, out *v1beta2.FileDiscovery, s conversion.Scope) error { + out.KubeConfigPath = in.KubeConfigPath + // WARNING: in.KubeConfig requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.FileDiscoveryKubeConfig vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.FileDiscoveryKubeConfig) + return nil +} + +func autoConvert_v1beta2_FileDiscovery_To_v1beta1_FileDiscovery(in *v1beta2.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { + out.KubeConfigPath = in.KubeConfigPath + // WARNING: in.KubeConfig requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.FileDiscoveryKubeConfig vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.FileDiscoveryKubeConfig) + return nil +} + +func autoConvert_v1beta1_FileDiscoveryKubeConfig_To_v1beta2_FileDiscoveryKubeConfig(in *FileDiscoveryKubeConfig, out *v1beta2.FileDiscoveryKubeConfig, s conversion.Scope) error { + // WARNING: in.Cluster requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.KubeConfigCluster vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.KubeConfigCluster) + if err := Convert_v1beta1_KubeConfigUser_To_v1beta2_KubeConfigUser(&in.User, &out.User, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1beta2_FileDiscoveryKubeConfig_To_v1beta1_FileDiscoveryKubeConfig(in *v1beta2.FileDiscoveryKubeConfig, out *FileDiscoveryKubeConfig, s conversion.Scope) error { + // WARNING: in.Cluster requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.KubeConfigCluster vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.KubeConfigCluster) + if err := Convert_v1beta2_KubeConfigUser_To_v1beta1_KubeConfigUser(&in.User, &out.User, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1beta1_FileSource_To_v1beta2_FileSource(in *FileSource, out *v1beta2.FileSource, s conversion.Scope) error { + if err := Convert_v1beta1_SecretFileSource_To_v1beta2_SecretFileSource(&in.Secret, &out.Secret, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_FileSource_To_v1beta2_FileSource is an autogenerated conversion function. +func Convert_v1beta1_FileSource_To_v1beta2_FileSource(in *FileSource, out *v1beta2.FileSource, s conversion.Scope) error { + return autoConvert_v1beta1_FileSource_To_v1beta2_FileSource(in, out, s) +} + +func autoConvert_v1beta2_FileSource_To_v1beta1_FileSource(in *v1beta2.FileSource, out *FileSource, s conversion.Scope) error { + if err := Convert_v1beta2_SecretFileSource_To_v1beta1_SecretFileSource(&in.Secret, &out.Secret, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_FileSource_To_v1beta1_FileSource is an autogenerated conversion function. +func Convert_v1beta2_FileSource_To_v1beta1_FileSource(in *v1beta2.FileSource, out *FileSource, s conversion.Scope) error { + return autoConvert_v1beta2_FileSource_To_v1beta1_FileSource(in, out, s) +} + +func autoConvert_v1beta1_Filesystem_To_v1beta2_Filesystem(in *Filesystem, out *v1beta2.Filesystem, s conversion.Scope) error { + out.Device = in.Device + out.Filesystem = in.Filesystem + out.Label = in.Label + if err := v1.Convert_Pointer_string_To_string(&in.Partition, &out.Partition, s); err != nil { + return err + } + out.Overwrite = (*bool)(unsafe.Pointer(in.Overwrite)) + if err := v1.Convert_Pointer_string_To_string(&in.ReplaceFS, &out.ReplaceFS, s); err != nil { + return err + } + out.ExtraOpts = *(*[]string)(unsafe.Pointer(&in.ExtraOpts)) + return nil +} + +// Convert_v1beta1_Filesystem_To_v1beta2_Filesystem is an autogenerated conversion function. +func Convert_v1beta1_Filesystem_To_v1beta2_Filesystem(in *Filesystem, out *v1beta2.Filesystem, s conversion.Scope) error { + return autoConvert_v1beta1_Filesystem_To_v1beta2_Filesystem(in, out, s) +} + +func autoConvert_v1beta2_Filesystem_To_v1beta1_Filesystem(in *v1beta2.Filesystem, out *Filesystem, s conversion.Scope) error { + out.Device = in.Device + out.Filesystem = in.Filesystem + out.Label = in.Label + if err := v1.Convert_string_To_Pointer_string(&in.Partition, &out.Partition, s); err != nil { + return err + } + out.Overwrite = (*bool)(unsafe.Pointer(in.Overwrite)) + if err := v1.Convert_string_To_Pointer_string(&in.ReplaceFS, &out.ReplaceFS, s); err != nil { + return err + } + out.ExtraOpts = *(*[]string)(unsafe.Pointer(&in.ExtraOpts)) + return nil +} + +// Convert_v1beta2_Filesystem_To_v1beta1_Filesystem is an autogenerated conversion function. +func Convert_v1beta2_Filesystem_To_v1beta1_Filesystem(in *v1beta2.Filesystem, out *Filesystem, s conversion.Scope) error { + return autoConvert_v1beta2_Filesystem_To_v1beta1_Filesystem(in, out, s) +} + +func autoConvert_v1beta1_HostPathMount_To_v1beta2_HostPathMount(in *HostPathMount, out *v1beta2.HostPathMount, s conversion.Scope) error { + out.Name = in.Name + out.HostPath = in.HostPath + out.MountPath = in.MountPath + if err := v1.Convert_bool_To_Pointer_bool(&in.ReadOnly, &out.ReadOnly, s); err != nil { + return err + } + out.PathType = corev1.HostPathType(in.PathType) + return nil +} + +// Convert_v1beta1_HostPathMount_To_v1beta2_HostPathMount is an autogenerated conversion function. +func Convert_v1beta1_HostPathMount_To_v1beta2_HostPathMount(in *HostPathMount, out *v1beta2.HostPathMount, s conversion.Scope) error { + return autoConvert_v1beta1_HostPathMount_To_v1beta2_HostPathMount(in, out, s) +} + +func autoConvert_v1beta2_HostPathMount_To_v1beta1_HostPathMount(in *v1beta2.HostPathMount, out *HostPathMount, s conversion.Scope) error { + out.Name = in.Name + out.HostPath = in.HostPath + out.MountPath = in.MountPath + if err := v1.Convert_Pointer_bool_To_bool(&in.ReadOnly, &out.ReadOnly, s); err != nil { + return err + } + out.PathType = corev1.HostPathType(in.PathType) + return nil +} + +// Convert_v1beta2_HostPathMount_To_v1beta1_HostPathMount is an autogenerated conversion function. +func Convert_v1beta2_HostPathMount_To_v1beta1_HostPathMount(in *v1beta2.HostPathMount, out *HostPathMount, s conversion.Scope) error { + return autoConvert_v1beta2_HostPathMount_To_v1beta1_HostPathMount(in, out, s) +} + +func autoConvert_v1beta1_IgnitionSpec_To_v1beta2_IgnitionSpec(in *IgnitionSpec, out *v1beta2.IgnitionSpec, s conversion.Scope) error { + // WARNING: in.ContainerLinuxConfig requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.ContainerLinuxConfig vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.ContainerLinuxConfig) + return nil +} + +func autoConvert_v1beta2_IgnitionSpec_To_v1beta1_IgnitionSpec(in *v1beta2.IgnitionSpec, out *IgnitionSpec, s conversion.Scope) error { + // WARNING: in.ContainerLinuxConfig requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.ContainerLinuxConfig vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.ContainerLinuxConfig) + return nil +} + +func autoConvert_v1beta1_InitConfiguration_To_v1beta2_InitConfiguration(in *InitConfiguration, out *v1beta2.InitConfiguration, s conversion.Scope) error { + // WARNING: in.TypeMeta requires manual conversion: does not exist in peer-type + if in.BootstrapTokens != nil { + in, out := &in.BootstrapTokens, &out.BootstrapTokens + *out = make([]v1beta2.BootstrapToken, len(*in)) + for i := range *in { + if err := Convert_v1beta1_BootstrapToken_To_v1beta2_BootstrapToken(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.BootstrapTokens = nil + } + if err := Convert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { + return err + } + if err := Convert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil { + return err + } + out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) + // WARNING: in.Patches requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.Patches vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Patches) + return nil +} + +func autoConvert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in *v1beta2.InitConfiguration, out *InitConfiguration, s conversion.Scope) error { + if in.BootstrapTokens != nil { + in, out := &in.BootstrapTokens, &out.BootstrapTokens + *out = make([]BootstrapToken, len(*in)) + for i := range *in { + if err := Convert_v1beta2_BootstrapToken_To_v1beta1_BootstrapToken(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.BootstrapTokens = nil + } + if err := Convert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { + return err + } + if err := Convert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil { + return err + } + out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) + // WARNING: in.Patches requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Patches vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.Patches) + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(in *JoinConfiguration, out *v1beta2.JoinConfiguration, s conversion.Scope) error { + // WARNING: in.TypeMeta requires manual conversion: does not exist in peer-type + if err := Convert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { + return err + } + out.CACertPath = in.CACertPath + if err := Convert_v1beta1_Discovery_To_v1beta2_Discovery(&in.Discovery, &out.Discovery, s); err != nil { + return err + } + out.ControlPlane = (*v1beta2.JoinControlPlane)(unsafe.Pointer(in.ControlPlane)) + out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) + // WARNING: in.Patches requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.Patches vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Patches) + return nil +} + +func autoConvert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(in *v1beta2.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error { + if err := Convert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { + return err + } + out.CACertPath = in.CACertPath + if err := Convert_v1beta2_Discovery_To_v1beta1_Discovery(&in.Discovery, &out.Discovery, s); err != nil { + return err + } + out.ControlPlane = (*JoinControlPlane)(unsafe.Pointer(in.ControlPlane)) + out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) + // WARNING: in.Patches requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Patches vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.Patches) + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_JoinControlPlane_To_v1beta2_JoinControlPlane(in *JoinControlPlane, out *v1beta2.JoinControlPlane, s conversion.Scope) error { + if err := Convert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_JoinControlPlane_To_v1beta2_JoinControlPlane is an autogenerated conversion function. +func Convert_v1beta1_JoinControlPlane_To_v1beta2_JoinControlPlane(in *JoinControlPlane, out *v1beta2.JoinControlPlane, s conversion.Scope) error { + return autoConvert_v1beta1_JoinControlPlane_To_v1beta2_JoinControlPlane(in, out, s) +} + +func autoConvert_v1beta2_JoinControlPlane_To_v1beta1_JoinControlPlane(in *v1beta2.JoinControlPlane, out *JoinControlPlane, s conversion.Scope) error { + if err := Convert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_JoinControlPlane_To_v1beta1_JoinControlPlane is an autogenerated conversion function. +func Convert_v1beta2_JoinControlPlane_To_v1beta1_JoinControlPlane(in *v1beta2.JoinControlPlane, out *JoinControlPlane, s conversion.Scope) error { + return autoConvert_v1beta2_JoinControlPlane_To_v1beta1_JoinControlPlane(in, out, s) +} + +func autoConvert_v1beta1_KubeConfigAuthExec_To_v1beta2_KubeConfigAuthExec(in *KubeConfigAuthExec, out *v1beta2.KubeConfigAuthExec, s conversion.Scope) error { + out.Command = in.Command + out.Args = *(*[]string)(unsafe.Pointer(&in.Args)) + out.Env = *(*[]v1beta2.KubeConfigAuthExecEnv)(unsafe.Pointer(&in.Env)) + out.APIVersion = in.APIVersion + if err := v1.Convert_bool_To_Pointer_bool(&in.ProvideClusterInfo, &out.ProvideClusterInfo, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_KubeConfigAuthExec_To_v1beta2_KubeConfigAuthExec is an autogenerated conversion function. +func Convert_v1beta1_KubeConfigAuthExec_To_v1beta2_KubeConfigAuthExec(in *KubeConfigAuthExec, out *v1beta2.KubeConfigAuthExec, s conversion.Scope) error { + return autoConvert_v1beta1_KubeConfigAuthExec_To_v1beta2_KubeConfigAuthExec(in, out, s) +} + +func autoConvert_v1beta2_KubeConfigAuthExec_To_v1beta1_KubeConfigAuthExec(in *v1beta2.KubeConfigAuthExec, out *KubeConfigAuthExec, s conversion.Scope) error { + out.Command = in.Command + out.Args = *(*[]string)(unsafe.Pointer(&in.Args)) + out.Env = *(*[]KubeConfigAuthExecEnv)(unsafe.Pointer(&in.Env)) + out.APIVersion = in.APIVersion + if err := v1.Convert_Pointer_bool_To_bool(&in.ProvideClusterInfo, &out.ProvideClusterInfo, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_KubeConfigAuthExec_To_v1beta1_KubeConfigAuthExec is an autogenerated conversion function. +func Convert_v1beta2_KubeConfigAuthExec_To_v1beta1_KubeConfigAuthExec(in *v1beta2.KubeConfigAuthExec, out *KubeConfigAuthExec, s conversion.Scope) error { + return autoConvert_v1beta2_KubeConfigAuthExec_To_v1beta1_KubeConfigAuthExec(in, out, s) +} + +func autoConvert_v1beta1_KubeConfigAuthExecEnv_To_v1beta2_KubeConfigAuthExecEnv(in *KubeConfigAuthExecEnv, out *v1beta2.KubeConfigAuthExecEnv, s conversion.Scope) error { + out.Name = in.Name + out.Value = in.Value + return nil +} + +// Convert_v1beta1_KubeConfigAuthExecEnv_To_v1beta2_KubeConfigAuthExecEnv is an autogenerated conversion function. +func Convert_v1beta1_KubeConfigAuthExecEnv_To_v1beta2_KubeConfigAuthExecEnv(in *KubeConfigAuthExecEnv, out *v1beta2.KubeConfigAuthExecEnv, s conversion.Scope) error { + return autoConvert_v1beta1_KubeConfigAuthExecEnv_To_v1beta2_KubeConfigAuthExecEnv(in, out, s) +} + +func autoConvert_v1beta2_KubeConfigAuthExecEnv_To_v1beta1_KubeConfigAuthExecEnv(in *v1beta2.KubeConfigAuthExecEnv, out *KubeConfigAuthExecEnv, s conversion.Scope) error { + out.Name = in.Name + out.Value = in.Value + return nil +} + +// Convert_v1beta2_KubeConfigAuthExecEnv_To_v1beta1_KubeConfigAuthExecEnv is an autogenerated conversion function. +func Convert_v1beta2_KubeConfigAuthExecEnv_To_v1beta1_KubeConfigAuthExecEnv(in *v1beta2.KubeConfigAuthExecEnv, out *KubeConfigAuthExecEnv, s conversion.Scope) error { + return autoConvert_v1beta2_KubeConfigAuthExecEnv_To_v1beta1_KubeConfigAuthExecEnv(in, out, s) +} + +func autoConvert_v1beta1_KubeConfigAuthProvider_To_v1beta2_KubeConfigAuthProvider(in *KubeConfigAuthProvider, out *v1beta2.KubeConfigAuthProvider, s conversion.Scope) error { + out.Name = in.Name + out.Config = *(*map[string]string)(unsafe.Pointer(&in.Config)) + return nil +} + +// Convert_v1beta1_KubeConfigAuthProvider_To_v1beta2_KubeConfigAuthProvider is an autogenerated conversion function. +func Convert_v1beta1_KubeConfigAuthProvider_To_v1beta2_KubeConfigAuthProvider(in *KubeConfigAuthProvider, out *v1beta2.KubeConfigAuthProvider, s conversion.Scope) error { + return autoConvert_v1beta1_KubeConfigAuthProvider_To_v1beta2_KubeConfigAuthProvider(in, out, s) +} + +func autoConvert_v1beta2_KubeConfigAuthProvider_To_v1beta1_KubeConfigAuthProvider(in *v1beta2.KubeConfigAuthProvider, out *KubeConfigAuthProvider, s conversion.Scope) error { + out.Name = in.Name + out.Config = *(*map[string]string)(unsafe.Pointer(&in.Config)) + return nil +} + +// Convert_v1beta2_KubeConfigAuthProvider_To_v1beta1_KubeConfigAuthProvider is an autogenerated conversion function. +func Convert_v1beta2_KubeConfigAuthProvider_To_v1beta1_KubeConfigAuthProvider(in *v1beta2.KubeConfigAuthProvider, out *KubeConfigAuthProvider, s conversion.Scope) error { + return autoConvert_v1beta2_KubeConfigAuthProvider_To_v1beta1_KubeConfigAuthProvider(in, out, s) +} + +func autoConvert_v1beta1_KubeConfigCluster_To_v1beta2_KubeConfigCluster(in *KubeConfigCluster, out *v1beta2.KubeConfigCluster, s conversion.Scope) error { + out.Server = in.Server + out.TLSServerName = in.TLSServerName + if err := v1.Convert_bool_To_Pointer_bool(&in.InsecureSkipTLSVerify, &out.InsecureSkipTLSVerify, s); err != nil { + return err + } + out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData)) + out.ProxyURL = in.ProxyURL + return nil +} + +// Convert_v1beta1_KubeConfigCluster_To_v1beta2_KubeConfigCluster is an autogenerated conversion function. +func Convert_v1beta1_KubeConfigCluster_To_v1beta2_KubeConfigCluster(in *KubeConfigCluster, out *v1beta2.KubeConfigCluster, s conversion.Scope) error { + return autoConvert_v1beta1_KubeConfigCluster_To_v1beta2_KubeConfigCluster(in, out, s) +} + +func autoConvert_v1beta2_KubeConfigCluster_To_v1beta1_KubeConfigCluster(in *v1beta2.KubeConfigCluster, out *KubeConfigCluster, s conversion.Scope) error { + out.Server = in.Server + out.TLSServerName = in.TLSServerName + if err := v1.Convert_Pointer_bool_To_bool(&in.InsecureSkipTLSVerify, &out.InsecureSkipTLSVerify, s); err != nil { + return err + } + out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData)) + out.ProxyURL = in.ProxyURL + return nil +} + +// Convert_v1beta2_KubeConfigCluster_To_v1beta1_KubeConfigCluster is an autogenerated conversion function. +func Convert_v1beta2_KubeConfigCluster_To_v1beta1_KubeConfigCluster(in *v1beta2.KubeConfigCluster, out *KubeConfigCluster, s conversion.Scope) error { + return autoConvert_v1beta2_KubeConfigCluster_To_v1beta1_KubeConfigCluster(in, out, s) +} + +func autoConvert_v1beta1_KubeConfigUser_To_v1beta2_KubeConfigUser(in *KubeConfigUser, out *v1beta2.KubeConfigUser, s conversion.Scope) error { + // WARNING: in.AuthProvider requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.KubeConfigAuthProvider vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.KubeConfigAuthProvider) + // WARNING: in.Exec requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.KubeConfigAuthExec vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.KubeConfigAuthExec) + return nil +} + +func autoConvert_v1beta2_KubeConfigUser_To_v1beta1_KubeConfigUser(in *v1beta2.KubeConfigUser, out *KubeConfigUser, s conversion.Scope) error { + // WARNING: in.AuthProvider requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.KubeConfigAuthProvider vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.KubeConfigAuthProvider) + // WARNING: in.Exec requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.KubeConfigAuthExec vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.KubeConfigAuthExec) + return nil +} + +func autoConvert_v1beta1_KubeadmConfig_To_v1beta2_KubeadmConfig(in *KubeadmConfig, out *v1beta2.KubeadmConfig, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta1_KubeadmConfigStatus_To_v1beta2_KubeadmConfigStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_KubeadmConfig_To_v1beta2_KubeadmConfig is an autogenerated conversion function. +func Convert_v1beta1_KubeadmConfig_To_v1beta2_KubeadmConfig(in *KubeadmConfig, out *v1beta2.KubeadmConfig, s conversion.Scope) error { + return autoConvert_v1beta1_KubeadmConfig_To_v1beta2_KubeadmConfig(in, out, s) +} + +func autoConvert_v1beta2_KubeadmConfig_To_v1beta1_KubeadmConfig(in *v1beta2.KubeadmConfig, out *KubeadmConfig, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_KubeadmConfigSpec_To_v1beta1_KubeadmConfigSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_KubeadmConfigStatus_To_v1beta1_KubeadmConfigStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_KubeadmConfig_To_v1beta1_KubeadmConfig is an autogenerated conversion function. +func Convert_v1beta2_KubeadmConfig_To_v1beta1_KubeadmConfig(in *v1beta2.KubeadmConfig, out *KubeadmConfig, s conversion.Scope) error { + return autoConvert_v1beta2_KubeadmConfig_To_v1beta1_KubeadmConfig(in, out, s) +} + +func autoConvert_v1beta1_KubeadmConfigList_To_v1beta2_KubeadmConfigList(in *KubeadmConfigList, out *v1beta2.KubeadmConfigList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta2.KubeadmConfig, len(*in)) + for i := range *in { + if err := Convert_v1beta1_KubeadmConfig_To_v1beta2_KubeadmConfig(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta1_KubeadmConfigList_To_v1beta2_KubeadmConfigList is an autogenerated conversion function. +func Convert_v1beta1_KubeadmConfigList_To_v1beta2_KubeadmConfigList(in *KubeadmConfigList, out *v1beta2.KubeadmConfigList, s conversion.Scope) error { + return autoConvert_v1beta1_KubeadmConfigList_To_v1beta2_KubeadmConfigList(in, out, s) +} + +func autoConvert_v1beta2_KubeadmConfigList_To_v1beta1_KubeadmConfigList(in *v1beta2.KubeadmConfigList, out *KubeadmConfigList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KubeadmConfig, len(*in)) + for i := range *in { + if err := Convert_v1beta2_KubeadmConfig_To_v1beta1_KubeadmConfig(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_KubeadmConfigList_To_v1beta1_KubeadmConfigList is an autogenerated conversion function. +func Convert_v1beta2_KubeadmConfigList_To_v1beta1_KubeadmConfigList(in *v1beta2.KubeadmConfigList, out *KubeadmConfigList, s conversion.Scope) error { + return autoConvert_v1beta2_KubeadmConfigList_To_v1beta1_KubeadmConfigList(in, out, s) +} + +func autoConvert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(in *KubeadmConfigSpec, out *v1beta2.KubeadmConfigSpec, s conversion.Scope) error { + // WARNING: in.ClusterConfiguration requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.ClusterConfiguration vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.ClusterConfiguration) + // WARNING: in.InitConfiguration requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.InitConfiguration vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.InitConfiguration) + // WARNING: in.JoinConfiguration requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.JoinConfiguration vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.JoinConfiguration) + if in.Files != nil { + in, out := &in.Files, &out.Files + *out = make([]v1beta2.File, len(*in)) + for i := range *in { + if err := Convert_v1beta1_File_To_v1beta2_File(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Files = nil + } + // WARNING: in.DiskSetup requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.DiskSetup vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.DiskSetup) + out.Mounts = *(*[]v1beta2.MountPoints)(unsafe.Pointer(&in.Mounts)) + out.BootCommands = *(*[]string)(unsafe.Pointer(&in.BootCommands)) + out.PreKubeadmCommands = *(*[]string)(unsafe.Pointer(&in.PreKubeadmCommands)) + out.PostKubeadmCommands = *(*[]string)(unsafe.Pointer(&in.PostKubeadmCommands)) + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make([]v1beta2.User, len(*in)) + for i := range *in { + if err := Convert_v1beta1_User_To_v1beta2_User(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Users = nil + } + // WARNING: in.NTP requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.NTP vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.NTP) + out.Format = v1beta2.Format(in.Format) + out.Verbosity = (*int32)(unsafe.Pointer(in.Verbosity)) + // WARNING: in.UseExperimentalRetryJoin requires manual conversion: does not exist in peer-type + // WARNING: in.Ignition requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.IgnitionSpec vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.IgnitionSpec) + return nil +} + +func autoConvert_v1beta2_KubeadmConfigSpec_To_v1beta1_KubeadmConfigSpec(in *v1beta2.KubeadmConfigSpec, out *KubeadmConfigSpec, s conversion.Scope) error { + // WARNING: in.ClusterConfiguration requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.ClusterConfiguration vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.ClusterConfiguration) + // WARNING: in.InitConfiguration requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.InitConfiguration vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.InitConfiguration) + // WARNING: in.JoinConfiguration requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.JoinConfiguration vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.JoinConfiguration) + if in.Files != nil { + in, out := &in.Files, &out.Files + *out = make([]File, len(*in)) + for i := range *in { + if err := Convert_v1beta2_File_To_v1beta1_File(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Files = nil + } + // WARNING: in.DiskSetup requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.DiskSetup vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.DiskSetup) + out.Mounts = *(*[]MountPoints)(unsafe.Pointer(&in.Mounts)) + out.BootCommands = *(*[]string)(unsafe.Pointer(&in.BootCommands)) + out.PreKubeadmCommands = *(*[]string)(unsafe.Pointer(&in.PreKubeadmCommands)) + out.PostKubeadmCommands = *(*[]string)(unsafe.Pointer(&in.PostKubeadmCommands)) + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make([]User, len(*in)) + for i := range *in { + if err := Convert_v1beta2_User_To_v1beta1_User(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Users = nil + } + // WARNING: in.NTP requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.NTP vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.NTP) + out.Format = Format(in.Format) + out.Verbosity = (*int32)(unsafe.Pointer(in.Verbosity)) + // WARNING: in.Ignition requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.IgnitionSpec vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.IgnitionSpec) + return nil +} + +func autoConvert_v1beta1_KubeadmConfigStatus_To_v1beta2_KubeadmConfigStatus(in *KubeadmConfigStatus, out *v1beta2.KubeadmConfigStatus, s conversion.Scope) error { + // WARNING: in.Ready requires manual conversion: does not exist in peer-type + if err := v1.Convert_Pointer_string_To_string(&in.DataSecretName, &out.DataSecretName, s); err != nil { + return err + } + // WARNING: in.FailureReason requires manual conversion: does not exist in peer-type + // WARNING: in.FailureMessage requires manual conversion: does not exist in peer-type + out.ObservedGeneration = in.ObservedGeneration + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + if err := Convert_v1beta1_Condition_To_v1_Condition(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Conditions = nil + } + // WARNING: in.V1Beta2 requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_KubeadmConfigStatus_To_v1beta1_KubeadmConfigStatus(in *v1beta2.KubeadmConfigStatus, out *KubeadmConfigStatus, s conversion.Scope) error { + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(corev1beta1.Conditions, len(*in)) + for i := range *in { + if err := Convert_v1_Condition_To_v1beta1_Condition(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Conditions = nil + } + // WARNING: in.Initialization requires manual conversion: does not exist in peer-type + if err := v1.Convert_string_To_Pointer_string(&in.DataSecretName, &out.DataSecretName, s); err != nil { + return err + } + out.ObservedGeneration = in.ObservedGeneration + // WARNING: in.Deprecated requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_KubeadmConfigTemplate_To_v1beta2_KubeadmConfigTemplate(in *KubeadmConfigTemplate, out *v1beta2.KubeadmConfigTemplate, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta1_KubeadmConfigTemplateSpec_To_v1beta2_KubeadmConfigTemplateSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_KubeadmConfigTemplate_To_v1beta2_KubeadmConfigTemplate is an autogenerated conversion function. +func Convert_v1beta1_KubeadmConfigTemplate_To_v1beta2_KubeadmConfigTemplate(in *KubeadmConfigTemplate, out *v1beta2.KubeadmConfigTemplate, s conversion.Scope) error { + return autoConvert_v1beta1_KubeadmConfigTemplate_To_v1beta2_KubeadmConfigTemplate(in, out, s) +} + +func autoConvert_v1beta2_KubeadmConfigTemplate_To_v1beta1_KubeadmConfigTemplate(in *v1beta2.KubeadmConfigTemplate, out *KubeadmConfigTemplate, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_KubeadmConfigTemplateSpec_To_v1beta1_KubeadmConfigTemplateSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_KubeadmConfigTemplate_To_v1beta1_KubeadmConfigTemplate is an autogenerated conversion function. +func Convert_v1beta2_KubeadmConfigTemplate_To_v1beta1_KubeadmConfigTemplate(in *v1beta2.KubeadmConfigTemplate, out *KubeadmConfigTemplate, s conversion.Scope) error { + return autoConvert_v1beta2_KubeadmConfigTemplate_To_v1beta1_KubeadmConfigTemplate(in, out, s) +} + +func autoConvert_v1beta1_KubeadmConfigTemplateList_To_v1beta2_KubeadmConfigTemplateList(in *KubeadmConfigTemplateList, out *v1beta2.KubeadmConfigTemplateList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta2.KubeadmConfigTemplate, len(*in)) + for i := range *in { + if err := Convert_v1beta1_KubeadmConfigTemplate_To_v1beta2_KubeadmConfigTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta1_KubeadmConfigTemplateList_To_v1beta2_KubeadmConfigTemplateList is an autogenerated conversion function. +func Convert_v1beta1_KubeadmConfigTemplateList_To_v1beta2_KubeadmConfigTemplateList(in *KubeadmConfigTemplateList, out *v1beta2.KubeadmConfigTemplateList, s conversion.Scope) error { + return autoConvert_v1beta1_KubeadmConfigTemplateList_To_v1beta2_KubeadmConfigTemplateList(in, out, s) +} + +func autoConvert_v1beta2_KubeadmConfigTemplateList_To_v1beta1_KubeadmConfigTemplateList(in *v1beta2.KubeadmConfigTemplateList, out *KubeadmConfigTemplateList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KubeadmConfigTemplate, len(*in)) + for i := range *in { + if err := Convert_v1beta2_KubeadmConfigTemplate_To_v1beta1_KubeadmConfigTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_KubeadmConfigTemplateList_To_v1beta1_KubeadmConfigTemplateList is an autogenerated conversion function. +func Convert_v1beta2_KubeadmConfigTemplateList_To_v1beta1_KubeadmConfigTemplateList(in *v1beta2.KubeadmConfigTemplateList, out *KubeadmConfigTemplateList, s conversion.Scope) error { + return autoConvert_v1beta2_KubeadmConfigTemplateList_To_v1beta1_KubeadmConfigTemplateList(in, out, s) +} + +func autoConvert_v1beta1_KubeadmConfigTemplateResource_To_v1beta2_KubeadmConfigTemplateResource(in *KubeadmConfigTemplateResource, out *v1beta2.KubeadmConfigTemplateResource, s conversion.Scope) error { + if err := Convert_v1beta1_ObjectMeta_To_v1beta2_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_KubeadmConfigTemplateResource_To_v1beta2_KubeadmConfigTemplateResource is an autogenerated conversion function. +func Convert_v1beta1_KubeadmConfigTemplateResource_To_v1beta2_KubeadmConfigTemplateResource(in *KubeadmConfigTemplateResource, out *v1beta2.KubeadmConfigTemplateResource, s conversion.Scope) error { + return autoConvert_v1beta1_KubeadmConfigTemplateResource_To_v1beta2_KubeadmConfigTemplateResource(in, out, s) +} + +func autoConvert_v1beta2_KubeadmConfigTemplateResource_To_v1beta1_KubeadmConfigTemplateResource(in *v1beta2.KubeadmConfigTemplateResource, out *KubeadmConfigTemplateResource, s conversion.Scope) error { + if err := Convert_v1beta2_ObjectMeta_To_v1beta1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1beta2_KubeadmConfigSpec_To_v1beta1_KubeadmConfigSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_KubeadmConfigTemplateResource_To_v1beta1_KubeadmConfigTemplateResource is an autogenerated conversion function. +func Convert_v1beta2_KubeadmConfigTemplateResource_To_v1beta1_KubeadmConfigTemplateResource(in *v1beta2.KubeadmConfigTemplateResource, out *KubeadmConfigTemplateResource, s conversion.Scope) error { + return autoConvert_v1beta2_KubeadmConfigTemplateResource_To_v1beta1_KubeadmConfigTemplateResource(in, out, s) +} + +func autoConvert_v1beta1_KubeadmConfigTemplateSpec_To_v1beta2_KubeadmConfigTemplateSpec(in *KubeadmConfigTemplateSpec, out *v1beta2.KubeadmConfigTemplateSpec, s conversion.Scope) error { + if err := Convert_v1beta1_KubeadmConfigTemplateResource_To_v1beta2_KubeadmConfigTemplateResource(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_KubeadmConfigTemplateSpec_To_v1beta2_KubeadmConfigTemplateSpec is an autogenerated conversion function. +func Convert_v1beta1_KubeadmConfigTemplateSpec_To_v1beta2_KubeadmConfigTemplateSpec(in *KubeadmConfigTemplateSpec, out *v1beta2.KubeadmConfigTemplateSpec, s conversion.Scope) error { + return autoConvert_v1beta1_KubeadmConfigTemplateSpec_To_v1beta2_KubeadmConfigTemplateSpec(in, out, s) +} + +func autoConvert_v1beta2_KubeadmConfigTemplateSpec_To_v1beta1_KubeadmConfigTemplateSpec(in *v1beta2.KubeadmConfigTemplateSpec, out *KubeadmConfigTemplateSpec, s conversion.Scope) error { + if err := Convert_v1beta2_KubeadmConfigTemplateResource_To_v1beta1_KubeadmConfigTemplateResource(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_KubeadmConfigTemplateSpec_To_v1beta1_KubeadmConfigTemplateSpec is an autogenerated conversion function. +func Convert_v1beta2_KubeadmConfigTemplateSpec_To_v1beta1_KubeadmConfigTemplateSpec(in *v1beta2.KubeadmConfigTemplateSpec, out *KubeadmConfigTemplateSpec, s conversion.Scope) error { + return autoConvert_v1beta2_KubeadmConfigTemplateSpec_To_v1beta1_KubeadmConfigTemplateSpec(in, out, s) +} + +func autoConvert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *v1beta2.LocalEtcd, s conversion.Scope) error { + // WARNING: in.ImageMeta requires manual conversion: does not exist in peer-type + out.DataDir = in.DataDir + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) + // WARNING: in.ExtraEnvs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.EnvVar vs *[]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.EnvVar) + out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) + out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) + return nil +} + +func autoConvert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(in *v1beta2.LocalEtcd, out *LocalEtcd, s conversion.Scope) error { + // WARNING: in.ImageRepository requires manual conversion: does not exist in peer-type + // WARNING: in.ImageTag requires manual conversion: does not exist in peer-type + out.DataDir = in.DataDir + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) + // WARNING: in.ExtraEnvs requires manual conversion: inconvertible types (*[]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.EnvVar vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.EnvVar) + out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) + out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) + return nil +} + +func autoConvert_v1beta1_NTP_To_v1beta2_NTP(in *NTP, out *v1beta2.NTP, s conversion.Scope) error { + out.Servers = *(*[]string)(unsafe.Pointer(&in.Servers)) + out.Enabled = (*bool)(unsafe.Pointer(in.Enabled)) + return nil +} + +// Convert_v1beta1_NTP_To_v1beta2_NTP is an autogenerated conversion function. +func Convert_v1beta1_NTP_To_v1beta2_NTP(in *NTP, out *v1beta2.NTP, s conversion.Scope) error { + return autoConvert_v1beta1_NTP_To_v1beta2_NTP(in, out, s) +} + +func autoConvert_v1beta2_NTP_To_v1beta1_NTP(in *v1beta2.NTP, out *NTP, s conversion.Scope) error { + out.Servers = *(*[]string)(unsafe.Pointer(&in.Servers)) + out.Enabled = (*bool)(unsafe.Pointer(in.Enabled)) + return nil +} + +// Convert_v1beta2_NTP_To_v1beta1_NTP is an autogenerated conversion function. +func Convert_v1beta2_NTP_To_v1beta1_NTP(in *v1beta2.NTP, out *NTP, s conversion.Scope) error { + return autoConvert_v1beta2_NTP_To_v1beta1_NTP(in, out, s) +} + +func autoConvert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *v1beta2.NodeRegistrationOptions, s conversion.Scope) error { + out.Name = in.Name + out.CRISocket = in.CRISocket + // WARNING: in.Taints requires manual conversion: inconvertible types ([]k8s.io/api/core/v1.Taint vs *[]k8s.io/api/core/v1.Taint) + // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) + out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) + out.ImagePullPolicy = corev1.PullPolicy(in.ImagePullPolicy) + out.ImagePullSerial = (*bool)(unsafe.Pointer(in.ImagePullSerial)) + return nil +} + +func autoConvert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in *v1beta2.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error { + out.Name = in.Name + out.CRISocket = in.CRISocket + // WARNING: in.Taints requires manual conversion: inconvertible types (*[]k8s.io/api/core/v1.Taint vs []k8s.io/api/core/v1.Taint) + // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) + out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) + out.ImagePullPolicy = string(in.ImagePullPolicy) + out.ImagePullSerial = (*bool)(unsafe.Pointer(in.ImagePullSerial)) + return nil +} + +func autoConvert_v1beta1_Partition_To_v1beta2_Partition(in *Partition, out *v1beta2.Partition, s conversion.Scope) error { + out.Device = in.Device + if err := v1.Convert_bool_To_Pointer_bool(&in.Layout, &out.Layout, s); err != nil { + return err + } + out.Overwrite = (*bool)(unsafe.Pointer(in.Overwrite)) + if err := v1.Convert_Pointer_string_To_string(&in.TableType, &out.TableType, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_Partition_To_v1beta2_Partition is an autogenerated conversion function. +func Convert_v1beta1_Partition_To_v1beta2_Partition(in *Partition, out *v1beta2.Partition, s conversion.Scope) error { + return autoConvert_v1beta1_Partition_To_v1beta2_Partition(in, out, s) +} + +func autoConvert_v1beta2_Partition_To_v1beta1_Partition(in *v1beta2.Partition, out *Partition, s conversion.Scope) error { + out.Device = in.Device + if err := v1.Convert_Pointer_bool_To_bool(&in.Layout, &out.Layout, s); err != nil { + return err + } + out.Overwrite = (*bool)(unsafe.Pointer(in.Overwrite)) + if err := v1.Convert_string_To_Pointer_string(&in.TableType, &out.TableType, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_Partition_To_v1beta1_Partition is an autogenerated conversion function. +func Convert_v1beta2_Partition_To_v1beta1_Partition(in *v1beta2.Partition, out *Partition, s conversion.Scope) error { + return autoConvert_v1beta2_Partition_To_v1beta1_Partition(in, out, s) +} + +func autoConvert_v1beta1_PasswdSource_To_v1beta2_PasswdSource(in *PasswdSource, out *v1beta2.PasswdSource, s conversion.Scope) error { + if err := Convert_v1beta1_SecretPasswdSource_To_v1beta2_SecretPasswdSource(&in.Secret, &out.Secret, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_PasswdSource_To_v1beta2_PasswdSource is an autogenerated conversion function. +func Convert_v1beta1_PasswdSource_To_v1beta2_PasswdSource(in *PasswdSource, out *v1beta2.PasswdSource, s conversion.Scope) error { + return autoConvert_v1beta1_PasswdSource_To_v1beta2_PasswdSource(in, out, s) +} + +func autoConvert_v1beta2_PasswdSource_To_v1beta1_PasswdSource(in *v1beta2.PasswdSource, out *PasswdSource, s conversion.Scope) error { + if err := Convert_v1beta2_SecretPasswdSource_To_v1beta1_SecretPasswdSource(&in.Secret, &out.Secret, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_PasswdSource_To_v1beta1_PasswdSource is an autogenerated conversion function. +func Convert_v1beta2_PasswdSource_To_v1beta1_PasswdSource(in *v1beta2.PasswdSource, out *PasswdSource, s conversion.Scope) error { + return autoConvert_v1beta2_PasswdSource_To_v1beta1_PasswdSource(in, out, s) +} + +func autoConvert_v1beta1_Patches_To_v1beta2_Patches(in *Patches, out *v1beta2.Patches, s conversion.Scope) error { + out.Directory = in.Directory + return nil +} + +// Convert_v1beta1_Patches_To_v1beta2_Patches is an autogenerated conversion function. +func Convert_v1beta1_Patches_To_v1beta2_Patches(in *Patches, out *v1beta2.Patches, s conversion.Scope) error { + return autoConvert_v1beta1_Patches_To_v1beta2_Patches(in, out, s) +} + +func autoConvert_v1beta2_Patches_To_v1beta1_Patches(in *v1beta2.Patches, out *Patches, s conversion.Scope) error { + out.Directory = in.Directory + return nil +} + +// Convert_v1beta2_Patches_To_v1beta1_Patches is an autogenerated conversion function. +func Convert_v1beta2_Patches_To_v1beta1_Patches(in *v1beta2.Patches, out *Patches, s conversion.Scope) error { + return autoConvert_v1beta2_Patches_To_v1beta1_Patches(in, out, s) +} + +func autoConvert_v1beta1_SecretFileSource_To_v1beta2_SecretFileSource(in *SecretFileSource, out *v1beta2.SecretFileSource, s conversion.Scope) error { + out.Name = in.Name + out.Key = in.Key + return nil +} + +// Convert_v1beta1_SecretFileSource_To_v1beta2_SecretFileSource is an autogenerated conversion function. +func Convert_v1beta1_SecretFileSource_To_v1beta2_SecretFileSource(in *SecretFileSource, out *v1beta2.SecretFileSource, s conversion.Scope) error { + return autoConvert_v1beta1_SecretFileSource_To_v1beta2_SecretFileSource(in, out, s) +} + +func autoConvert_v1beta2_SecretFileSource_To_v1beta1_SecretFileSource(in *v1beta2.SecretFileSource, out *SecretFileSource, s conversion.Scope) error { + out.Name = in.Name + out.Key = in.Key + return nil +} + +// Convert_v1beta2_SecretFileSource_To_v1beta1_SecretFileSource is an autogenerated conversion function. +func Convert_v1beta2_SecretFileSource_To_v1beta1_SecretFileSource(in *v1beta2.SecretFileSource, out *SecretFileSource, s conversion.Scope) error { + return autoConvert_v1beta2_SecretFileSource_To_v1beta1_SecretFileSource(in, out, s) +} + +func autoConvert_v1beta1_SecretPasswdSource_To_v1beta2_SecretPasswdSource(in *SecretPasswdSource, out *v1beta2.SecretPasswdSource, s conversion.Scope) error { + out.Name = in.Name + out.Key = in.Key + return nil +} + +// Convert_v1beta1_SecretPasswdSource_To_v1beta2_SecretPasswdSource is an autogenerated conversion function. +func Convert_v1beta1_SecretPasswdSource_To_v1beta2_SecretPasswdSource(in *SecretPasswdSource, out *v1beta2.SecretPasswdSource, s conversion.Scope) error { + return autoConvert_v1beta1_SecretPasswdSource_To_v1beta2_SecretPasswdSource(in, out, s) +} + +func autoConvert_v1beta2_SecretPasswdSource_To_v1beta1_SecretPasswdSource(in *v1beta2.SecretPasswdSource, out *SecretPasswdSource, s conversion.Scope) error { + out.Name = in.Name + out.Key = in.Key + return nil +} + +// Convert_v1beta2_SecretPasswdSource_To_v1beta1_SecretPasswdSource is an autogenerated conversion function. +func Convert_v1beta2_SecretPasswdSource_To_v1beta1_SecretPasswdSource(in *v1beta2.SecretPasswdSource, out *SecretPasswdSource, s conversion.Scope) error { + return autoConvert_v1beta2_SecretPasswdSource_To_v1beta1_SecretPasswdSource(in, out, s) +} + +func autoConvert_v1beta1_User_To_v1beta2_User(in *User, out *v1beta2.User, s conversion.Scope) error { + out.Name = in.Name + if err := v1.Convert_Pointer_string_To_string(&in.Gecos, &out.Gecos, s); err != nil { + return err + } + if err := v1.Convert_Pointer_string_To_string(&in.Groups, &out.Groups, s); err != nil { + return err + } + if err := v1.Convert_Pointer_string_To_string(&in.HomeDir, &out.HomeDir, s); err != nil { + return err + } + out.Inactive = (*bool)(unsafe.Pointer(in.Inactive)) + if err := v1.Convert_Pointer_string_To_string(&in.Shell, &out.Shell, s); err != nil { + return err + } + if err := v1.Convert_Pointer_string_To_string(&in.Passwd, &out.Passwd, s); err != nil { + return err + } + // WARNING: in.PasswdFrom requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.PasswdSource vs sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.PasswdSource) + if err := v1.Convert_Pointer_string_To_string(&in.PrimaryGroup, &out.PrimaryGroup, s); err != nil { + return err + } + out.LockPassword = (*bool)(unsafe.Pointer(in.LockPassword)) + if err := v1.Convert_Pointer_string_To_string(&in.Sudo, &out.Sudo, s); err != nil { + return err + } + out.SSHAuthorizedKeys = *(*[]string)(unsafe.Pointer(&in.SSHAuthorizedKeys)) + return nil +} + +func autoConvert_v1beta2_User_To_v1beta1_User(in *v1beta2.User, out *User, s conversion.Scope) error { + out.Name = in.Name + if err := v1.Convert_string_To_Pointer_string(&in.Gecos, &out.Gecos, s); err != nil { + return err + } + if err := v1.Convert_string_To_Pointer_string(&in.Groups, &out.Groups, s); err != nil { + return err + } + if err := v1.Convert_string_To_Pointer_string(&in.HomeDir, &out.HomeDir, s); err != nil { + return err + } + out.Inactive = (*bool)(unsafe.Pointer(in.Inactive)) + if err := v1.Convert_string_To_Pointer_string(&in.Shell, &out.Shell, s); err != nil { + return err + } + if err := v1.Convert_string_To_Pointer_string(&in.Passwd, &out.Passwd, s); err != nil { + return err + } + // WARNING: in.PasswdFrom requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.PasswdSource vs *sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1.PasswdSource) + if err := v1.Convert_string_To_Pointer_string(&in.PrimaryGroup, &out.PrimaryGroup, s); err != nil { + return err + } + out.LockPassword = (*bool)(unsafe.Pointer(in.LockPassword)) + if err := v1.Convert_string_To_Pointer_string(&in.Sudo, &out.Sudo, s); err != nil { + return err + } + out.SSHAuthorizedKeys = *(*[]string)(unsafe.Pointer(&in.SSHAuthorizedKeys)) + return nil +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/zz_generated.deepcopy.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..0bb8529818 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,1353 @@ +//go:build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + corev1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIEndpoint) DeepCopyInto(out *APIEndpoint) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIEndpoint. +func (in *APIEndpoint) DeepCopy() *APIEndpoint { + if in == nil { + return nil + } + out := new(APIEndpoint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIServer) DeepCopyInto(out *APIServer) { + *out = *in + in.ControlPlaneComponent.DeepCopyInto(&out.ControlPlaneComponent) + if in.CertSANs != nil { + in, out := &in.CertSANs, &out.CertSANs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.TimeoutForControlPlane != nil { + in, out := &in.TimeoutForControlPlane, &out.TimeoutForControlPlane + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIServer. +func (in *APIServer) DeepCopy() *APIServer { + if in == nil { + return nil + } + out := new(APIServer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BootstrapToken) DeepCopyInto(out *BootstrapToken) { + *out = *in + if in.Token != nil { + in, out := &in.Token, &out.Token + *out = new(BootstrapTokenString) + **out = **in + } + if in.TTL != nil { + in, out := &in.TTL, &out.TTL + *out = new(v1.Duration) + **out = **in + } + if in.Expires != nil { + in, out := &in.Expires, &out.Expires + *out = (*in).DeepCopy() + } + if in.Usages != nil { + in, out := &in.Usages, &out.Usages + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Groups != nil { + in, out := &in.Groups, &out.Groups + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapToken. +func (in *BootstrapToken) DeepCopy() *BootstrapToken { + if in == nil { + return nil + } + out := new(BootstrapToken) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BootstrapTokenDiscovery) DeepCopyInto(out *BootstrapTokenDiscovery) { + *out = *in + if in.CACertHashes != nil { + in, out := &in.CACertHashes, &out.CACertHashes + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapTokenDiscovery. +func (in *BootstrapTokenDiscovery) DeepCopy() *BootstrapTokenDiscovery { + if in == nil { + return nil + } + out := new(BootstrapTokenDiscovery) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BootstrapTokenString) DeepCopyInto(out *BootstrapTokenString) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapTokenString. +func (in *BootstrapTokenString) DeepCopy() *BootstrapTokenString { + if in == nil { + return nil + } + out := new(BootstrapTokenString) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterConfiguration) DeepCopyInto(out *ClusterConfiguration) { + *out = *in + out.TypeMeta = in.TypeMeta + in.Etcd.DeepCopyInto(&out.Etcd) + out.Networking = in.Networking + in.APIServer.DeepCopyInto(&out.APIServer) + in.ControllerManager.DeepCopyInto(&out.ControllerManager) + in.Scheduler.DeepCopyInto(&out.Scheduler) + out.DNS = in.DNS + if in.FeatureGates != nil { + in, out := &in.FeatureGates, &out.FeatureGates + *out = make(map[string]bool, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterConfiguration. +func (in *ClusterConfiguration) DeepCopy() *ClusterConfiguration { + if in == nil { + return nil + } + out := new(ClusterConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterConfiguration) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterStatus) DeepCopyInto(out *ClusterStatus) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.APIEndpoints != nil { + in, out := &in.APIEndpoints, &out.APIEndpoints + *out = make(map[string]APIEndpoint, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterStatus. +func (in *ClusterStatus) DeepCopy() *ClusterStatus { + if in == nil { + return nil + } + out := new(ClusterStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterStatus) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerLinuxConfig) DeepCopyInto(out *ContainerLinuxConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerLinuxConfig. +func (in *ContainerLinuxConfig) DeepCopy() *ContainerLinuxConfig { + if in == nil { + return nil + } + out := new(ContainerLinuxConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControlPlaneComponent) DeepCopyInto(out *ControlPlaneComponent) { + *out = *in + if in.ExtraArgs != nil { + in, out := &in.ExtraArgs, &out.ExtraArgs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ExtraVolumes != nil { + in, out := &in.ExtraVolumes, &out.ExtraVolumes + *out = make([]HostPathMount, len(*in)) + copy(*out, *in) + } + if in.ExtraEnvs != nil { + in, out := &in.ExtraEnvs, &out.ExtraEnvs + *out = make([]EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneComponent. +func (in *ControlPlaneComponent) DeepCopy() *ControlPlaneComponent { + if in == nil { + return nil + } + out := new(ControlPlaneComponent) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DNS) DeepCopyInto(out *DNS) { + *out = *in + out.ImageMeta = in.ImageMeta +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNS. +func (in *DNS) DeepCopy() *DNS { + if in == nil { + return nil + } + out := new(DNS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Discovery) DeepCopyInto(out *Discovery) { + *out = *in + if in.BootstrapToken != nil { + in, out := &in.BootstrapToken, &out.BootstrapToken + *out = new(BootstrapTokenDiscovery) + (*in).DeepCopyInto(*out) + } + if in.File != nil { + in, out := &in.File, &out.File + *out = new(FileDiscovery) + (*in).DeepCopyInto(*out) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Discovery. +func (in *Discovery) DeepCopy() *Discovery { + if in == nil { + return nil + } + out := new(Discovery) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DiskSetup) DeepCopyInto(out *DiskSetup) { + *out = *in + if in.Partitions != nil { + in, out := &in.Partitions, &out.Partitions + *out = make([]Partition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Filesystems != nil { + in, out := &in.Filesystems, &out.Filesystems + *out = make([]Filesystem, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiskSetup. +func (in *DiskSetup) DeepCopy() *DiskSetup { + if in == nil { + return nil + } + out := new(DiskSetup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvVar) DeepCopyInto(out *EnvVar) { + *out = *in + in.EnvVar.DeepCopyInto(&out.EnvVar) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvVar. +func (in *EnvVar) DeepCopy() *EnvVar { + if in == nil { + return nil + } + out := new(EnvVar) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Etcd) DeepCopyInto(out *Etcd) { + *out = *in + if in.Local != nil { + in, out := &in.Local, &out.Local + *out = new(LocalEtcd) + (*in).DeepCopyInto(*out) + } + if in.External != nil { + in, out := &in.External, &out.External + *out = new(ExternalEtcd) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Etcd. +func (in *Etcd) DeepCopy() *Etcd { + if in == nil { + return nil + } + out := new(Etcd) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExternalEtcd) DeepCopyInto(out *ExternalEtcd) { + *out = *in + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalEtcd. +func (in *ExternalEtcd) DeepCopy() *ExternalEtcd { + if in == nil { + return nil + } + out := new(ExternalEtcd) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *File) DeepCopyInto(out *File) { + *out = *in + if in.ContentFrom != nil { + in, out := &in.ContentFrom, &out.ContentFrom + *out = new(FileSource) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new File. +func (in *File) DeepCopy() *File { + if in == nil { + return nil + } + out := new(File) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FileDiscovery) DeepCopyInto(out *FileDiscovery) { + *out = *in + if in.KubeConfig != nil { + in, out := &in.KubeConfig, &out.KubeConfig + *out = new(FileDiscoveryKubeConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileDiscovery. +func (in *FileDiscovery) DeepCopy() *FileDiscovery { + if in == nil { + return nil + } + out := new(FileDiscovery) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FileDiscoveryKubeConfig) DeepCopyInto(out *FileDiscoveryKubeConfig) { + *out = *in + if in.Cluster != nil { + in, out := &in.Cluster, &out.Cluster + *out = new(KubeConfigCluster) + (*in).DeepCopyInto(*out) + } + in.User.DeepCopyInto(&out.User) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileDiscoveryKubeConfig. +func (in *FileDiscoveryKubeConfig) DeepCopy() *FileDiscoveryKubeConfig { + if in == nil { + return nil + } + out := new(FileDiscoveryKubeConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FileSource) DeepCopyInto(out *FileSource) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileSource. +func (in *FileSource) DeepCopy() *FileSource { + if in == nil { + return nil + } + out := new(FileSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Filesystem) DeepCopyInto(out *Filesystem) { + *out = *in + if in.Partition != nil { + in, out := &in.Partition, &out.Partition + *out = new(string) + **out = **in + } + if in.Overwrite != nil { + in, out := &in.Overwrite, &out.Overwrite + *out = new(bool) + **out = **in + } + if in.ReplaceFS != nil { + in, out := &in.ReplaceFS, &out.ReplaceFS + *out = new(string) + **out = **in + } + if in.ExtraOpts != nil { + in, out := &in.ExtraOpts, &out.ExtraOpts + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Filesystem. +func (in *Filesystem) DeepCopy() *Filesystem { + if in == nil { + return nil + } + out := new(Filesystem) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HostPathMount) DeepCopyInto(out *HostPathMount) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostPathMount. +func (in *HostPathMount) DeepCopy() *HostPathMount { + if in == nil { + return nil + } + out := new(HostPathMount) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IgnitionSpec) DeepCopyInto(out *IgnitionSpec) { + *out = *in + if in.ContainerLinuxConfig != nil { + in, out := &in.ContainerLinuxConfig, &out.ContainerLinuxConfig + *out = new(ContainerLinuxConfig) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IgnitionSpec. +func (in *IgnitionSpec) DeepCopy() *IgnitionSpec { + if in == nil { + return nil + } + out := new(IgnitionSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageMeta) DeepCopyInto(out *ImageMeta) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageMeta. +func (in *ImageMeta) DeepCopy() *ImageMeta { + if in == nil { + return nil + } + out := new(ImageMeta) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InitConfiguration) DeepCopyInto(out *InitConfiguration) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.BootstrapTokens != nil { + in, out := &in.BootstrapTokens, &out.BootstrapTokens + *out = make([]BootstrapToken, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.NodeRegistration.DeepCopyInto(&out.NodeRegistration) + out.LocalAPIEndpoint = in.LocalAPIEndpoint + if in.SkipPhases != nil { + in, out := &in.SkipPhases, &out.SkipPhases + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Patches != nil { + in, out := &in.Patches, &out.Patches + *out = new(Patches) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InitConfiguration. +func (in *InitConfiguration) DeepCopy() *InitConfiguration { + if in == nil { + return nil + } + out := new(InitConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InitConfiguration) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JoinConfiguration) DeepCopyInto(out *JoinConfiguration) { + *out = *in + out.TypeMeta = in.TypeMeta + in.NodeRegistration.DeepCopyInto(&out.NodeRegistration) + in.Discovery.DeepCopyInto(&out.Discovery) + if in.ControlPlane != nil { + in, out := &in.ControlPlane, &out.ControlPlane + *out = new(JoinControlPlane) + **out = **in + } + if in.SkipPhases != nil { + in, out := &in.SkipPhases, &out.SkipPhases + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Patches != nil { + in, out := &in.Patches, &out.Patches + *out = new(Patches) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JoinConfiguration. +func (in *JoinConfiguration) DeepCopy() *JoinConfiguration { + if in == nil { + return nil + } + out := new(JoinConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *JoinConfiguration) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JoinControlPlane) DeepCopyInto(out *JoinControlPlane) { + *out = *in + out.LocalAPIEndpoint = in.LocalAPIEndpoint +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JoinControlPlane. +func (in *JoinControlPlane) DeepCopy() *JoinControlPlane { + if in == nil { + return nil + } + out := new(JoinControlPlane) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeConfigAuthExec) DeepCopyInto(out *KubeConfigAuthExec) { + *out = *in + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]KubeConfigAuthExecEnv, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfigAuthExec. +func (in *KubeConfigAuthExec) DeepCopy() *KubeConfigAuthExec { + if in == nil { + return nil + } + out := new(KubeConfigAuthExec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeConfigAuthExecEnv) DeepCopyInto(out *KubeConfigAuthExecEnv) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfigAuthExecEnv. +func (in *KubeConfigAuthExecEnv) DeepCopy() *KubeConfigAuthExecEnv { + if in == nil { + return nil + } + out := new(KubeConfigAuthExecEnv) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeConfigAuthProvider) DeepCopyInto(out *KubeConfigAuthProvider) { + *out = *in + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfigAuthProvider. +func (in *KubeConfigAuthProvider) DeepCopy() *KubeConfigAuthProvider { + if in == nil { + return nil + } + out := new(KubeConfigAuthProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeConfigCluster) DeepCopyInto(out *KubeConfigCluster) { + *out = *in + if in.CertificateAuthorityData != nil { + in, out := &in.CertificateAuthorityData, &out.CertificateAuthorityData + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfigCluster. +func (in *KubeConfigCluster) DeepCopy() *KubeConfigCluster { + if in == nil { + return nil + } + out := new(KubeConfigCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeConfigUser) DeepCopyInto(out *KubeConfigUser) { + *out = *in + if in.AuthProvider != nil { + in, out := &in.AuthProvider, &out.AuthProvider + *out = new(KubeConfigAuthProvider) + (*in).DeepCopyInto(*out) + } + if in.Exec != nil { + in, out := &in.Exec, &out.Exec + *out = new(KubeConfigAuthExec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfigUser. +func (in *KubeConfigUser) DeepCopy() *KubeConfigUser { + if in == nil { + return nil + } + out := new(KubeConfigUser) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmConfig) DeepCopyInto(out *KubeadmConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmConfig. +func (in *KubeadmConfig) DeepCopy() *KubeadmConfig { + if in == nil { + return nil + } + out := new(KubeadmConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeadmConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmConfigList) DeepCopyInto(out *KubeadmConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KubeadmConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmConfigList. +func (in *KubeadmConfigList) DeepCopy() *KubeadmConfigList { + if in == nil { + return nil + } + out := new(KubeadmConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeadmConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmConfigSpec) DeepCopyInto(out *KubeadmConfigSpec) { + *out = *in + if in.ClusterConfiguration != nil { + in, out := &in.ClusterConfiguration, &out.ClusterConfiguration + *out = new(ClusterConfiguration) + (*in).DeepCopyInto(*out) + } + if in.InitConfiguration != nil { + in, out := &in.InitConfiguration, &out.InitConfiguration + *out = new(InitConfiguration) + (*in).DeepCopyInto(*out) + } + if in.JoinConfiguration != nil { + in, out := &in.JoinConfiguration, &out.JoinConfiguration + *out = new(JoinConfiguration) + (*in).DeepCopyInto(*out) + } + if in.Files != nil { + in, out := &in.Files, &out.Files + *out = make([]File, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.DiskSetup != nil { + in, out := &in.DiskSetup, &out.DiskSetup + *out = new(DiskSetup) + (*in).DeepCopyInto(*out) + } + if in.Mounts != nil { + in, out := &in.Mounts, &out.Mounts + *out = make([]MountPoints, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = make(MountPoints, len(*in)) + copy(*out, *in) + } + } + } + if in.BootCommands != nil { + in, out := &in.BootCommands, &out.BootCommands + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PreKubeadmCommands != nil { + in, out := &in.PreKubeadmCommands, &out.PreKubeadmCommands + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PostKubeadmCommands != nil { + in, out := &in.PostKubeadmCommands, &out.PostKubeadmCommands + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make([]User, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NTP != nil { + in, out := &in.NTP, &out.NTP + *out = new(NTP) + (*in).DeepCopyInto(*out) + } + if in.Verbosity != nil { + in, out := &in.Verbosity, &out.Verbosity + *out = new(int32) + **out = **in + } + if in.Ignition != nil { + in, out := &in.Ignition, &out.Ignition + *out = new(IgnitionSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmConfigSpec. +func (in *KubeadmConfigSpec) DeepCopy() *KubeadmConfigSpec { + if in == nil { + return nil + } + out := new(KubeadmConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmConfigStatus) DeepCopyInto(out *KubeadmConfigStatus) { + *out = *in + if in.DataSecretName != nil { + in, out := &in.DataSecretName, &out.DataSecretName + *out = new(string) + **out = **in + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(corev1beta1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.V1Beta2 != nil { + in, out := &in.V1Beta2, &out.V1Beta2 + *out = new(KubeadmConfigV1Beta2Status) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmConfigStatus. +func (in *KubeadmConfigStatus) DeepCopy() *KubeadmConfigStatus { + if in == nil { + return nil + } + out := new(KubeadmConfigStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmConfigTemplate) DeepCopyInto(out *KubeadmConfigTemplate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmConfigTemplate. +func (in *KubeadmConfigTemplate) DeepCopy() *KubeadmConfigTemplate { + if in == nil { + return nil + } + out := new(KubeadmConfigTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeadmConfigTemplate) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmConfigTemplateList) DeepCopyInto(out *KubeadmConfigTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KubeadmConfigTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmConfigTemplateList. +func (in *KubeadmConfigTemplateList) DeepCopy() *KubeadmConfigTemplateList { + if in == nil { + return nil + } + out := new(KubeadmConfigTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeadmConfigTemplateList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmConfigTemplateResource) DeepCopyInto(out *KubeadmConfigTemplateResource) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmConfigTemplateResource. +func (in *KubeadmConfigTemplateResource) DeepCopy() *KubeadmConfigTemplateResource { + if in == nil { + return nil + } + out := new(KubeadmConfigTemplateResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmConfigTemplateSpec) DeepCopyInto(out *KubeadmConfigTemplateSpec) { + *out = *in + in.Template.DeepCopyInto(&out.Template) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmConfigTemplateSpec. +func (in *KubeadmConfigTemplateSpec) DeepCopy() *KubeadmConfigTemplateSpec { + if in == nil { + return nil + } + out := new(KubeadmConfigTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmConfigV1Beta2Status) DeepCopyInto(out *KubeadmConfigV1Beta2Status) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmConfigV1Beta2Status. +func (in *KubeadmConfigV1Beta2Status) DeepCopy() *KubeadmConfigV1Beta2Status { + if in == nil { + return nil + } + out := new(KubeadmConfigV1Beta2Status) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalEtcd) DeepCopyInto(out *LocalEtcd) { + *out = *in + out.ImageMeta = in.ImageMeta + if in.ExtraArgs != nil { + in, out := &in.ExtraArgs, &out.ExtraArgs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ExtraEnvs != nil { + in, out := &in.ExtraEnvs, &out.ExtraEnvs + *out = make([]EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ServerCertSANs != nil { + in, out := &in.ServerCertSANs, &out.ServerCertSANs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PeerCertSANs != nil { + in, out := &in.PeerCertSANs, &out.PeerCertSANs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalEtcd. +func (in *LocalEtcd) DeepCopy() *LocalEtcd { + if in == nil { + return nil + } + out := new(LocalEtcd) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in MountPoints) DeepCopyInto(out *MountPoints) { + { + in := &in + *out = make(MountPoints, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MountPoints. +func (in MountPoints) DeepCopy() MountPoints { + if in == nil { + return nil + } + out := new(MountPoints) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NTP) DeepCopyInto(out *NTP) { + *out = *in + if in.Servers != nil { + in, out := &in.Servers, &out.Servers + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NTP. +func (in *NTP) DeepCopy() *NTP { + if in == nil { + return nil + } + out := new(NTP) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Networking) DeepCopyInto(out *Networking) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Networking. +func (in *Networking) DeepCopy() *Networking { + if in == nil { + return nil + } + out := new(Networking) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodeRegistrationOptions) DeepCopyInto(out *NodeRegistrationOptions) { + *out = *in + if in.Taints != nil { + in, out := &in.Taints, &out.Taints + *out = make([]corev1.Taint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.KubeletExtraArgs != nil { + in, out := &in.KubeletExtraArgs, &out.KubeletExtraArgs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.IgnorePreflightErrors != nil { + in, out := &in.IgnorePreflightErrors, &out.IgnorePreflightErrors + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ImagePullSerial != nil { + in, out := &in.ImagePullSerial, &out.ImagePullSerial + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeRegistrationOptions. +func (in *NodeRegistrationOptions) DeepCopy() *NodeRegistrationOptions { + if in == nil { + return nil + } + out := new(NodeRegistrationOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Partition) DeepCopyInto(out *Partition) { + *out = *in + if in.Overwrite != nil { + in, out := &in.Overwrite, &out.Overwrite + *out = new(bool) + **out = **in + } + if in.TableType != nil { + in, out := &in.TableType, &out.TableType + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Partition. +func (in *Partition) DeepCopy() *Partition { + if in == nil { + return nil + } + out := new(Partition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PasswdSource) DeepCopyInto(out *PasswdSource) { + *out = *in + out.Secret = in.Secret +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PasswdSource. +func (in *PasswdSource) DeepCopy() *PasswdSource { + if in == nil { + return nil + } + out := new(PasswdSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Patches) DeepCopyInto(out *Patches) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Patches. +func (in *Patches) DeepCopy() *Patches { + if in == nil { + return nil + } + out := new(Patches) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretFileSource) DeepCopyInto(out *SecretFileSource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretFileSource. +func (in *SecretFileSource) DeepCopy() *SecretFileSource { + if in == nil { + return nil + } + out := new(SecretFileSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretPasswdSource) DeepCopyInto(out *SecretPasswdSource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretPasswdSource. +func (in *SecretPasswdSource) DeepCopy() *SecretPasswdSource { + if in == nil { + return nil + } + out := new(SecretPasswdSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *User) DeepCopyInto(out *User) { + *out = *in + if in.Gecos != nil { + in, out := &in.Gecos, &out.Gecos + *out = new(string) + **out = **in + } + if in.Groups != nil { + in, out := &in.Groups, &out.Groups + *out = new(string) + **out = **in + } + if in.HomeDir != nil { + in, out := &in.HomeDir, &out.HomeDir + *out = new(string) + **out = **in + } + if in.Inactive != nil { + in, out := &in.Inactive, &out.Inactive + *out = new(bool) + **out = **in + } + if in.Shell != nil { + in, out := &in.Shell, &out.Shell + *out = new(string) + **out = **in + } + if in.Passwd != nil { + in, out := &in.Passwd, &out.Passwd + *out = new(string) + **out = **in + } + if in.PasswdFrom != nil { + in, out := &in.PasswdFrom, &out.PasswdFrom + *out = new(PasswdSource) + **out = **in + } + if in.PrimaryGroup != nil { + in, out := &in.PrimaryGroup, &out.PrimaryGroup + *out = new(string) + **out = **in + } + if in.LockPassword != nil { + in, out := &in.LockPassword, &out.LockPassword + *out = new(bool) + **out = **in + } + if in.Sudo != nil { + in, out := &in.Sudo, &out.Sudo + *out = new(string) + **out = **in + } + if in.SSHAuthorizedKeys != nil { + in, out := &in.SSHAuthorizedKeys, &out.SSHAuthorizedKeys + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. +func (in *User) DeepCopy() *User { + if in == nil { + return nil + } + out := new(User) + in.DeepCopyInto(out) + return out +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2/kubeadm_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2/kubeadm_types.go index e9f17e19a2..17e3bfc784 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2/kubeadm_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2/kubeadm_types.go @@ -72,6 +72,23 @@ const ( KubeadmConfigDataSecretNotAvailableReason = clusterv1.NotAvailableReason ) +// EncryptionAlgorithmType can define an asymmetric encryption algorithm type. +// +kubebuilder:validation:Enum=ECDSA-P256;ECDSA-P384;RSA-2048;RSA-3072;RSA-4096 +type EncryptionAlgorithmType string + +const ( + // EncryptionAlgorithmECDSAP256 defines the ECDSA encryption algorithm type with curve P256. + EncryptionAlgorithmECDSAP256 EncryptionAlgorithmType = "ECDSA-P256" + // EncryptionAlgorithmECDSAP384 defines the ECDSA encryption algorithm type with curve P384. + EncryptionAlgorithmECDSAP384 EncryptionAlgorithmType = "ECDSA-P384" + // EncryptionAlgorithmRSA2048 defines the RSA encryption algorithm type with key size 2048 bits. + EncryptionAlgorithmRSA2048 EncryptionAlgorithmType = "RSA-2048" + // EncryptionAlgorithmRSA3072 defines the RSA encryption algorithm type with key size 3072 bits. + EncryptionAlgorithmRSA3072 EncryptionAlgorithmType = "RSA-3072" + // EncryptionAlgorithmRSA4096 defines the RSA encryption algorithm type with key size 4096 bits. + EncryptionAlgorithmRSA4096 EncryptionAlgorithmType = "RSA-4096" +) + // InitConfiguration contains a list of elements that is specific "kubeadm init"-only runtime // information. // +kubebuilder:validation:MinProperties=1 @@ -174,16 +191,7 @@ type ClusterConfiguration struct { CertificatesDir string `json:"certificatesDir,omitempty"` // imageRepository sets the container registry to pull images from. - // * If not set, the default registry of kubeadm will be used, i.e. - // * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0 - // * k8s.gcr.io (old registry): all older versions - // Please note that when imageRepository is not set we don't allow upgrades to - // versions >= v1.22.0 which use the old registry (k8s.gcr.io). Please use - // a newer patch version with the new registry instead (i.e. >= v1.22.17, - // >= v1.23.15, >= v1.24.9, >= v1.25.0). - // * If the version is a CI build (kubernetes version starts with `ci/` or `ci-cross/`) - // `gcr.io/k8s-staging-ci-images` will be used as a default for control plane components - // and for kube-proxy, while `registry.k8s.io` will be used for all the other images. + // If not set, the default registry of kubeadm will be used (registry.k8s.io). // +optional // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=512 @@ -208,6 +216,16 @@ type ClusterConfiguration struct { // +kubebuilder:validation:Minimum=1 // +kubebuilder:validation:Maximum=36500 CACertificateValidityPeriodDays int32 `json:"caCertificateValidityPeriodDays,omitempty"` + + // encryptionAlgorithm holds the type of asymmetric encryption algorithm used for keys and certificates. + // Can be one of "RSA-2048", "RSA-3072", "RSA-4096", "ECDSA-P256" or "ECDSA-P384". + // For Kubernetes 1.34 or above, "ECDSA-P384" is supported. + // If not specified, Cluster API will use RSA-2048 as default. + // When this field is modified every certificate generated afterward will use the new + // encryptionAlgorithm. Existing CA certificates and service account keys are not rotated. + // This field is only supported with Kubernetes v1.31 or above. + // +optional + EncryptionAlgorithm EncryptionAlgorithmType `json:"encryptionAlgorithm,omitempty"` } // IsDefined returns true if the ClusterConfiguration is defined. @@ -356,6 +374,11 @@ type APIEndpoint struct { BindPort int32 `json:"bindPort,omitempty"` } +// IsDefined returns true if the APIEndpoint is defined. +func (r *APIEndpoint) IsDefined() bool { + return r.AdvertiseAddress != "" || r.BindPort != 0 +} + // NodeRegistrationOptions holds fields that relate to registering a new control-plane or node to the cluster, either via "kubeadm init" or "kubeadm join". // Note: The NodeRegistrationOptions struct has to be kept in sync with the structs in MarshalJSON. // +kubebuilder:validation:MinProperties=1 diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/.import-restrictions b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/.import-restrictions new file mode 100644 index 0000000000..f6f10b3ff5 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/.import-restrictions @@ -0,0 +1,5 @@ +rules: + - selectorRegexp: sigs[.]k8s[.]io/controller-runtime + allowedPrefixes: + - "sigs.k8s.io/controller-runtime/pkg/conversion" + forbiddenPrefixes: [] diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/condition_consts.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/condition_consts.go new file mode 100644 index 0000000000..c460c3a80f --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/condition_consts.go @@ -0,0 +1,153 @@ +/* +Copyright 2021 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 v1beta1 + +import ( + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" +) + +// Conditions and condition Reasons for the KubeadmControlPlane object. + +const ( + // MachinesReadyCondition reports an aggregate of current status of the machines controlled by the KubeadmControlPlane. + MachinesReadyCondition clusterv1beta1.ConditionType = "MachinesReady" +) + +const ( + // CertificatesAvailableCondition documents that cluster certificates were generated as part of the + // processing of a KubeadmControlPlane object. + CertificatesAvailableCondition clusterv1beta1.ConditionType = "CertificatesAvailable" + + // CertificatesGenerationFailedReason (Severity=Warning) documents a KubeadmControlPlane controller detecting + // an error while generating certificates; those kind of errors are usually temporary and the controller + // automatically recover from them. + CertificatesGenerationFailedReason = "CertificatesGenerationFailed" +) + +const ( + // AvailableCondition documents that the first control plane instance has completed the kubeadm init operation + // and so the control plane is available and an API server instance is ready for processing requests. + AvailableCondition clusterv1beta1.ConditionType = "Available" + + // WaitingForKubeadmInitReason (Severity=Info) documents a KubeadmControlPlane object waiting for the first + // control plane instance to complete the kubeadm init operation. + WaitingForKubeadmInitReason = "WaitingForKubeadmInit" +) + +const ( + // MachinesSpecUpToDateCondition documents that the spec of the machines controlled by the KubeadmControlPlane + // is up to date. When this condition is false, the KubeadmControlPlane is executing a rolling upgrade. + MachinesSpecUpToDateCondition clusterv1beta1.ConditionType = "MachinesSpecUpToDate" + + // RollingUpdateInProgressReason (Severity=Warning) documents a KubeadmControlPlane object executing a + // rolling upgrade for aligning the machines spec to the desired state. + RollingUpdateInProgressReason = "RollingUpdateInProgress" +) + +const ( + // ResizedCondition documents a KubeadmControlPlane that is resizing the set of controlled machines. + ResizedCondition clusterv1beta1.ConditionType = "Resized" + + // ScalingUpReason (Severity=Info) documents a KubeadmControlPlane that is increasing the number of replicas. + ScalingUpReason = "ScalingUp" + + // ScalingDownReason (Severity=Info) documents a KubeadmControlPlane that is decreasing the number of replicas. + ScalingDownReason = "ScalingDown" +) + +const ( + // ControlPlaneComponentsHealthyCondition reports the overall status of control plane components + // implemented as static pods generated by kubeadm including kube-api-server, kube-controller manager, + // kube-scheduler and etcd if managed. + ControlPlaneComponentsHealthyCondition clusterv1beta1.ConditionType = "ControlPlaneComponentsHealthy" + + // ControlPlaneComponentsUnhealthyReason (Severity=Error) documents a control plane component not healthy. + ControlPlaneComponentsUnhealthyReason = "ControlPlaneComponentsUnhealthy" + + // ControlPlaneComponentsUnknownReason reports a control plane component in unknown status. + ControlPlaneComponentsUnknownReason = "ControlPlaneComponentsUnknown" + + // ControlPlaneComponentsInspectionFailedReason documents a failure in inspecting the control plane component status. + ControlPlaneComponentsInspectionFailedReason = "ControlPlaneComponentsInspectionFailed" + + // MachineAPIServerPodHealthyCondition reports a machine's kube-apiserver's operational status. + MachineAPIServerPodHealthyCondition clusterv1beta1.ConditionType = "APIServerPodHealthy" + + // MachineControllerManagerPodHealthyCondition reports a machine's kube-controller-manager's health status. + MachineControllerManagerPodHealthyCondition clusterv1beta1.ConditionType = "ControllerManagerPodHealthy" + + // MachineSchedulerPodHealthyCondition reports a machine's kube-scheduler's operational status. + MachineSchedulerPodHealthyCondition clusterv1beta1.ConditionType = "SchedulerPodHealthy" + + // MachineEtcdPodHealthyCondition reports a machine's etcd pod's operational status. + // NOTE: This conditions exists only if a stacked etcd cluster is used. + MachineEtcdPodHealthyCondition clusterv1beta1.ConditionType = "EtcdPodHealthy" + + // PodProvisioningReason (Severity=Info) documents a pod waiting to be provisioned i.e., Pod is in "Pending" phase. + PodProvisioningReason = "PodProvisioning" + + // PodMissingReason (Severity=Error) documents a pod does not exist. + PodMissingReason = "PodMissing" + + // PodFailedReason (Severity=Error) documents if a pod failed during provisioning i.e., e.g CrashLoopbackOff, ImagePullBackOff + // or if all the containers in a pod have terminated. + PodFailedReason = "PodFailed" + + // PodInspectionFailedReason documents a failure in inspecting the pod status. + PodInspectionFailedReason = "PodInspectionFailed" +) + +const ( + // EtcdClusterHealthyCondition documents the overall etcd cluster's health. + EtcdClusterHealthyCondition clusterv1beta1.ConditionType = "EtcdClusterHealthy" + + // EtcdClusterInspectionFailedReason documents a failure in inspecting the etcd cluster status. + EtcdClusterInspectionFailedReason = "EtcdClusterInspectionFailed" + + // EtcdClusterUnknownReason reports an etcd cluster in unknown status. + EtcdClusterUnknownReason = "EtcdClusterUnknown" + + // EtcdClusterUnhealthyReason (Severity=Error) is set when the etcd cluster is unhealthy. + EtcdClusterUnhealthyReason = "EtcdClusterUnhealthy" + + // MachineEtcdMemberHealthyCondition report the machine's etcd member's health status. + // NOTE: This conditions exists only if a stacked etcd cluster is used. + MachineEtcdMemberHealthyCondition clusterv1beta1.ConditionType = "EtcdMemberHealthy" + + // EtcdMemberInspectionFailedReason documents a failure in inspecting the etcd member status. + EtcdMemberInspectionFailedReason = "MemberInspectionFailed" + + // EtcdMemberUnhealthyReason (Severity=Error) documents a Machine's etcd member is unhealthy. + EtcdMemberUnhealthyReason = "EtcdMemberUnhealthy" + + // MachinesCreatedCondition documents that the machines controlled by the KubeadmControlPlane are created. + // When this condition is false, it indicates that there was an error when cloning the infrastructure/bootstrap template or + // when generating the machine object. + MachinesCreatedCondition clusterv1beta1.ConditionType = "MachinesCreated" + + // InfrastructureTemplateCloningFailedReason (Severity=Error) documents a KubeadmControlPlane failing to + // clone the infrastructure template. + InfrastructureTemplateCloningFailedReason = "InfrastructureTemplateCloningFailed" + + // BootstrapTemplateCloningFailedReason (Severity=Error) documents a KubeadmControlPlane failing to + // clone the bootstrap template. + BootstrapTemplateCloningFailedReason = "BootstrapTemplateCloningFailed" + + // MachineGenerationFailedReason (Severity=Error) documents a KubeadmControlPlane failing to + // generate a machine object. + MachineGenerationFailedReason = "MachineGenerationFailed" +) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/conversion.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/conversion.go new file mode 100644 index 0000000000..df956c8d94 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/conversion.go @@ -0,0 +1,595 @@ +/* +Copyright 2021 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 v1beta1 + +import ( + "errors" + "fmt" + "reflect" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apimachineryconversion "k8s.io/apimachinery/pkg/conversion" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/conversion" + + bootstrapv1beta1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1" + bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" + controlplanev1 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2" + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" +) + +var apiVersionGetter = func(_ schema.GroupKind) (string, error) { + return "", errors.New("apiVersionGetter not set") +} + +func SetAPIVersionGetter(f func(gk schema.GroupKind) (string, error)) { + apiVersionGetter = f +} + +func (src *KubeadmControlPlane) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*controlplanev1.KubeadmControlPlane) + if err := Convert_v1beta1_KubeadmControlPlane_To_v1beta2_KubeadmControlPlane(src, dst, nil); err != nil { + return err + } + + infraRef, err := convertToContractVersionedObjectReference(&src.Spec.MachineTemplate.InfrastructureRef) + if err != nil { + return err + } + dst.Spec.MachineTemplate.Spec.InfrastructureRef = *infraRef + + // Manually restore data. + restored := &controlplanev1.KubeadmControlPlane{} + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { + return err + } + + // Recover intent for bool values converted to *bool. + initialization := controlplanev1.KubeadmControlPlaneInitializationStatus{} + restoredControlPlaneInitialized := restored.Status.Initialization.ControlPlaneInitialized + clusterv1.Convert_bool_To_Pointer_bool(src.Status.Initialized, ok, restoredControlPlaneInitialized, &initialization.ControlPlaneInitialized) + if !reflect.DeepEqual(initialization, controlplanev1.KubeadmControlPlaneInitializationStatus{}) { + dst.Status.Initialization = initialization + } + + if err := bootstrapv1beta1.RestoreBoolIntentKubeadmConfigSpec(&src.Spec.KubeadmConfigSpec, &dst.Spec.KubeadmConfigSpec, ok, &restored.Spec.KubeadmConfigSpec); err != nil { + return err + } + + // Recover other values + if ok { + bootstrapv1beta1.RestoreKubeadmConfigSpec(&restored.Spec.KubeadmConfigSpec, &dst.Spec.KubeadmConfigSpec) + } + + if src.Spec.RemediationStrategy != nil { + clusterv1.Convert_Duration_To_Pointer_int32(src.Spec.RemediationStrategy.RetryPeriod, ok, restored.Spec.Remediation.RetryPeriodSeconds, &dst.Spec.Remediation.RetryPeriodSeconds) + } + + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.KubeadmConfigSpec.ConvertTo(&dst.Spec.KubeadmConfigSpec) + return nil +} + +func (dst *KubeadmControlPlane) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*controlplanev1.KubeadmControlPlane) + if err := Convert_v1beta2_KubeadmControlPlane_To_v1beta1_KubeadmControlPlane(src, dst, nil); err != nil { + return err + } + + if src.Spec.MachineTemplate.Spec.InfrastructureRef.IsDefined() { + infraRef, err := convertToObjectReference(&src.Spec.MachineTemplate.Spec.InfrastructureRef, src.Namespace) + if err != nil { + return err + } + dst.Spec.MachineTemplate.InfrastructureRef = *infraRef + } + + // Convert timeouts moved from one struct to another. + dst.Spec.KubeadmConfigSpec.ConvertFrom(&src.Spec.KubeadmConfigSpec) + + dropEmptyStringsKubeadmConfigSpec(&dst.Spec.KubeadmConfigSpec) + dropEmptyStringsKubeadmControlPlaneStatus(&dst.Status) + + // Preserve Hub data on down-conversion except for metadata. + return utilconversion.MarshalData(src, dst) +} + +func (src *KubeadmControlPlaneTemplate) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*controlplanev1.KubeadmControlPlaneTemplate) + if err := Convert_v1beta1_KubeadmControlPlaneTemplate_To_v1beta2_KubeadmControlPlaneTemplate(src, dst, nil); err != nil { + return err + } + + // Manually restore data. + restored := &controlplanev1.KubeadmControlPlaneTemplate{} + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { + return err + } + + // Recover intent for bool values converted to *bool. + if err := bootstrapv1beta1.RestoreBoolIntentKubeadmConfigSpec(&src.Spec.Template.Spec.KubeadmConfigSpec, &dst.Spec.Template.Spec.KubeadmConfigSpec, ok, &restored.Spec.Template.Spec.KubeadmConfigSpec); err != nil { + return err + } + + // Recover other values + if ok { + bootstrapv1beta1.RestoreKubeadmConfigSpec(&restored.Spec.Template.Spec.KubeadmConfigSpec, &dst.Spec.Template.Spec.KubeadmConfigSpec) + } + + if src.Spec.Template.Spec.RemediationStrategy != nil { + clusterv1.Convert_Duration_To_Pointer_int32(src.Spec.Template.Spec.RemediationStrategy.RetryPeriod, ok, restored.Spec.Template.Spec.Remediation.RetryPeriodSeconds, &dst.Spec.Template.Spec.Remediation.RetryPeriodSeconds) + } + + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.Template.Spec.KubeadmConfigSpec.ConvertTo(&dst.Spec.Template.Spec.KubeadmConfigSpec) + return nil +} + +func (dst *KubeadmControlPlaneTemplate) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*controlplanev1.KubeadmControlPlaneTemplate) + if err := Convert_v1beta2_KubeadmControlPlaneTemplate_To_v1beta1_KubeadmControlPlaneTemplate(src, dst, nil); err != nil { + return err + } + + // Convert timeouts moved from one struct to another. + dst.Spec.Template.Spec.KubeadmConfigSpec.ConvertFrom(&src.Spec.Template.Spec.KubeadmConfigSpec) + + dropEmptyStringsKubeadmConfigSpec(&dst.Spec.Template.Spec.KubeadmConfigSpec) + + // Preserve Hub data on down-conversion except for metadata. + return utilconversion.MarshalData(src, dst) +} + +func Convert_v1beta2_KubeadmControlPlaneSpec_To_v1beta1_KubeadmControlPlaneSpec(in *controlplanev1.KubeadmControlPlaneSpec, out *KubeadmControlPlaneSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_KubeadmControlPlaneSpec_To_v1beta1_KubeadmControlPlaneSpec(in, out, s); err != nil { + return err + } + + if !reflect.DeepEqual(in.Remediation, controlplanev1.KubeadmControlPlaneRemediationSpec{}) { + out.RemediationStrategy = &RemediationStrategy{} + if err := Convert_v1beta2_KubeadmControlPlaneRemediationSpec_To_v1beta1_RemediationStrategy(&in.Remediation, out.RemediationStrategy, s); err != nil { + return err + } + } + if !reflect.DeepEqual(in.Rollout.Before, controlplanev1.KubeadmControlPlaneRolloutBeforeSpec{}) { + out.RolloutBefore = &RolloutBefore{} + out.RolloutBefore.CertificatesExpiryDays = ptr.To(in.Rollout.Before.CertificatesExpiryDays) + } + if !reflect.DeepEqual(in.Rollout.After, metav1.Time{}) { + out.RolloutAfter = ptr.To(in.Rollout.After) + } + if !reflect.DeepEqual(in.Rollout.Strategy, controlplanev1.KubeadmControlPlaneRolloutStrategy{}) { + out.RolloutStrategy = &RolloutStrategy{} + out.RolloutStrategy.Type = RolloutStrategyType(in.Rollout.Strategy.Type) + if in.Rollout.Strategy.RollingUpdate.MaxSurge != nil { + out.RolloutStrategy.RollingUpdate = &RollingUpdate{} + out.RolloutStrategy.RollingUpdate.MaxSurge = in.Rollout.Strategy.RollingUpdate.MaxSurge + } + } + if in.MachineNaming.Template != "" { + out.MachineNamingStrategy = &MachineNamingStrategy{ + Template: in.MachineNaming.Template, + } + } + + return nil +} + +func Convert_v1beta1_KubeadmControlPlaneSpec_To_v1beta2_KubeadmControlPlaneSpec(in *KubeadmControlPlaneSpec, out *controlplanev1.KubeadmControlPlaneSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_KubeadmControlPlaneSpec_To_v1beta2_KubeadmControlPlaneSpec(in, out, s); err != nil { + return err + } + + if in.RemediationStrategy != nil { + if err := Convert_v1beta1_RemediationStrategy_To_v1beta2_KubeadmControlPlaneRemediationSpec(in.RemediationStrategy, &out.Remediation, s); err != nil { + return err + } + } + if in.RolloutBefore != nil && in.RolloutBefore.CertificatesExpiryDays != nil { + out.Rollout.Before.CertificatesExpiryDays = *in.RolloutBefore.CertificatesExpiryDays + } + if in.RolloutAfter != nil && !reflect.DeepEqual(in.RolloutAfter, &metav1.Time{}) { + out.Rollout.After = *in.RolloutAfter + } + if in.RolloutStrategy != nil { + out.Rollout.Strategy.Type = controlplanev1.KubeadmControlPlaneRolloutStrategyType(in.RolloutStrategy.Type) + if in.RolloutStrategy.RollingUpdate != nil && in.RolloutStrategy.RollingUpdate.MaxSurge != nil { + out.Rollout.Strategy.RollingUpdate.MaxSurge = in.RolloutStrategy.RollingUpdate.MaxSurge + } + } + if in.MachineNamingStrategy != nil { + out.MachineNaming.Template = in.MachineNamingStrategy.Template + } + + return nil +} + +func Convert_v1beta2_KubeadmControlPlaneTemplateResourceSpec_To_v1beta1_KubeadmControlPlaneTemplateResourceSpec(in *controlplanev1.KubeadmControlPlaneTemplateResourceSpec, out *KubeadmControlPlaneTemplateResourceSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_KubeadmControlPlaneTemplateResourceSpec_To_v1beta1_KubeadmControlPlaneTemplateResourceSpec(in, out, s); err != nil { + return err + } + + if !reflect.DeepEqual(in.MachineTemplate, KubeadmControlPlaneTemplateMachineTemplate{}) { + out.MachineTemplate = &KubeadmControlPlaneTemplateMachineTemplate{} + if err := Convert_v1beta2_KubeadmControlPlaneTemplateMachineTemplate_To_v1beta1_KubeadmControlPlaneTemplateMachineTemplate(&in.MachineTemplate, out.MachineTemplate, s); err != nil { + return err + } + } + + if !reflect.DeepEqual(in.Remediation, controlplanev1.KubeadmControlPlaneRemediationSpec{}) { + out.RemediationStrategy = &RemediationStrategy{} + if err := Convert_v1beta2_KubeadmControlPlaneRemediationSpec_To_v1beta1_RemediationStrategy(&in.Remediation, out.RemediationStrategy, s); err != nil { + return err + } + } + if !reflect.DeepEqual(in.Rollout.Before, controlplanev1.KubeadmControlPlaneRolloutBeforeSpec{}) { + out.RolloutBefore = &RolloutBefore{} + out.RolloutBefore.CertificatesExpiryDays = ptr.To(in.Rollout.Before.CertificatesExpiryDays) + } + if !reflect.DeepEqual(in.Rollout.After, metav1.Time{}) { + out.RolloutAfter = ptr.To(in.Rollout.After) + } + if !reflect.DeepEqual(in.Rollout.Strategy, controlplanev1.KubeadmControlPlaneRolloutStrategy{}) { + out.RolloutStrategy = &RolloutStrategy{} + out.RolloutStrategy.Type = RolloutStrategyType(in.Rollout.Strategy.Type) + if in.Rollout.Strategy.RollingUpdate.MaxSurge != nil { + out.RolloutStrategy.RollingUpdate = &RollingUpdate{} + out.RolloutStrategy.RollingUpdate.MaxSurge = in.Rollout.Strategy.RollingUpdate.MaxSurge + } + } + if in.MachineNaming.Template != "" { + out.MachineNamingStrategy = &MachineNamingStrategy{ + Template: in.MachineNaming.Template, + } + } + + return nil +} + +func Convert_v1beta1_KubeadmControlPlaneTemplateResourceSpec_To_v1beta2_KubeadmControlPlaneTemplateResourceSpec(in *KubeadmControlPlaneTemplateResourceSpec, out *controlplanev1.KubeadmControlPlaneTemplateResourceSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_KubeadmControlPlaneTemplateResourceSpec_To_v1beta2_KubeadmControlPlaneTemplateResourceSpec(in, out, s); err != nil { + return err + } + + if in.MachineTemplate != nil { + if err := Convert_v1beta1_KubeadmControlPlaneTemplateMachineTemplate_To_v1beta2_KubeadmControlPlaneTemplateMachineTemplate(in.MachineTemplate, &out.MachineTemplate, s); err != nil { + return err + } + } + + if in.RemediationStrategy != nil { + if err := Convert_v1beta1_RemediationStrategy_To_v1beta2_KubeadmControlPlaneRemediationSpec(in.RemediationStrategy, &out.Remediation, s); err != nil { + return err + } + } + if in.RolloutBefore != nil && in.RolloutBefore.CertificatesExpiryDays != nil { + out.Rollout.Before.CertificatesExpiryDays = *in.RolloutBefore.CertificatesExpiryDays + } + if in.RolloutAfter != nil && !reflect.DeepEqual(in.RolloutAfter, &metav1.Time{}) { + out.Rollout.After = *in.RolloutAfter + } + if in.RolloutStrategy != nil { + // If Type is empty in v1beta1, set it to RollingUpdateStrategyType. + // This is the same behavior as in previous versions of Cluster API as Type + // was always defaulted to RollingUpdateStrategyType, and also RollingUpdateStrategyType + // is the only valid value. + if in.RolloutStrategy.Type == "" { + out.Rollout.Strategy.Type = controlplanev1.RollingUpdateStrategyType + } else { + out.Rollout.Strategy.Type = controlplanev1.KubeadmControlPlaneRolloutStrategyType(in.RolloutStrategy.Type) + } + if in.RolloutStrategy.RollingUpdate != nil && in.RolloutStrategy.RollingUpdate.MaxSurge != nil { + out.Rollout.Strategy.RollingUpdate.MaxSurge = in.RolloutStrategy.RollingUpdate.MaxSurge + } + } + if in.MachineNamingStrategy != nil { + out.MachineNaming.Template = in.MachineNamingStrategy.Template + } + + return nil +} + +func Convert_v1beta2_KubeadmControlPlaneStatus_To_v1beta1_KubeadmControlPlaneStatus(in *controlplanev1.KubeadmControlPlaneStatus, out *KubeadmControlPlaneStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_KubeadmControlPlaneStatus_To_v1beta1_KubeadmControlPlaneStatus(in, out, s); err != nil { + return err + } + + if !reflect.DeepEqual(in.LastRemediation, controlplanev1.LastRemediationStatus{}) { + out.LastRemediation = &LastRemediationStatus{} + if err := Convert_v1beta2_LastRemediationStatus_To_v1beta1_LastRemediationStatus(&in.LastRemediation, out.LastRemediation, s); err != nil { + return err + } + } + + // Reset conditions from autogenerated conversions + // NOTE: v1beta2 conditions should not be automatically be converted into legacy conditions (v1beta1). + out.Conditions = nil + + // Reset replica counters from autogenerated conversions + // NOTE: replica counters with a new semantic should not be automatically be converted into old replica counters. + out.ReadyReplicas = 0 + + // Retrieve legacy conditions (v1beta1), failureReason, failureMessage and replica counters from the deprecated field. + if in.Deprecated != nil && in.Deprecated.V1Beta1 != nil { + if in.Deprecated.V1Beta1.Conditions != nil { + clusterv1beta1.Convert_v1beta2_Deprecated_V1Beta1_Conditions_To_v1beta1_Conditions(&in.Deprecated.V1Beta1.Conditions, &out.Conditions) + } + out.FailureReason = in.Deprecated.V1Beta1.FailureReason + out.FailureMessage = in.Deprecated.V1Beta1.FailureMessage + out.UpdatedReplicas = in.Deprecated.V1Beta1.UpdatedReplicas + out.ReadyReplicas = in.Deprecated.V1Beta1.ReadyReplicas + out.UnavailableReplicas = in.Deprecated.V1Beta1.UnavailableReplicas + } + + // Move initialized to ControlPlaneInitialized, rebuild ready + out.Initialized = ptr.Deref(in.Initialization.ControlPlaneInitialized, false) + out.Ready = out.ReadyReplicas > 0 + + // Move new conditions (v1beta2) and replica counter to the v1beta2 field. + if in.Conditions == nil && in.ReadyReplicas == nil && in.AvailableReplicas == nil && in.UpToDateReplicas == nil { + return nil + } + out.V1Beta2 = &KubeadmControlPlaneV1Beta2Status{} + out.V1Beta2.Conditions = in.Conditions + out.V1Beta2.ReadyReplicas = in.ReadyReplicas + out.V1Beta2.AvailableReplicas = in.AvailableReplicas + out.V1Beta2.UpToDateReplicas = in.UpToDateReplicas + return nil +} + +func Convert_v1beta1_KubeadmControlPlaneStatus_To_v1beta2_KubeadmControlPlaneStatus(in *KubeadmControlPlaneStatus, out *controlplanev1.KubeadmControlPlaneStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_KubeadmControlPlaneStatus_To_v1beta2_KubeadmControlPlaneStatus(in, out, s); err != nil { + return err + } + + if in.LastRemediation != nil { + if err := Convert_v1beta1_LastRemediationStatus_To_v1beta2_LastRemediationStatus(in.LastRemediation, &out.LastRemediation, s); err != nil { + return err + } + } + + // Reset conditions from autogenerated conversions + // NOTE: v1beta1 conditions should not be automatically be converted into v1beta2 conditions. + out.Conditions = nil + + // Reset replica counters from autogenerated conversions + // NOTE: old replica counters should not be automatically be converted into replica counters with a new semantic. + out.ReadyReplicas = nil + + // Retrieve new conditions (v1beta2) and replica counter from the v1beta2 field. + if in.V1Beta2 != nil { + out.Conditions = in.V1Beta2.Conditions + out.ReadyReplicas = in.V1Beta2.ReadyReplicas + out.AvailableReplicas = in.V1Beta2.AvailableReplicas + out.UpToDateReplicas = in.V1Beta2.UpToDateReplicas + } + + // Move legacy conditions (v1beta1), failureReason, failureMessage and replica counters to the deprecated field. + if out.Deprecated == nil { + out.Deprecated = &controlplanev1.KubeadmControlPlaneDeprecatedStatus{} + } + if out.Deprecated.V1Beta1 == nil { + out.Deprecated.V1Beta1 = &controlplanev1.KubeadmControlPlaneV1Beta1DeprecatedStatus{} + } + if in.Conditions != nil { + clusterv1beta1.Convert_v1beta1_Conditions_To_v1beta2_Deprecated_V1Beta1_Conditions(&in.Conditions, &out.Deprecated.V1Beta1.Conditions) + } + out.Deprecated.V1Beta1.FailureReason = in.FailureReason + out.Deprecated.V1Beta1.FailureMessage = in.FailureMessage + out.Deprecated.V1Beta1.UpdatedReplicas = in.UpdatedReplicas + out.Deprecated.V1Beta1.ReadyReplicas = in.ReadyReplicas + out.Deprecated.V1Beta1.UnavailableReplicas = in.UnavailableReplicas + + // Move initialized to ControlPlaneInitialized is implemented in ConvertTo. + return nil +} + +func Convert_v1beta1_KubeadmControlPlaneMachineTemplate_To_v1beta2_KubeadmControlPlaneMachineTemplate(in *KubeadmControlPlaneMachineTemplate, out *controlplanev1.KubeadmControlPlaneMachineTemplate, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_KubeadmControlPlaneMachineTemplate_To_v1beta2_KubeadmControlPlaneMachineTemplate(in, out, s); err != nil { + return err + } + if in.ReadinessGates != nil { + in, out := &in.ReadinessGates, &out.Spec.ReadinessGates + *out = make([]clusterv1.MachineReadinessGate, len(*in)) + for i := range *in { + if err := clusterv1beta1.Convert_v1beta1_MachineReadinessGate_To_v1beta2_MachineReadinessGate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } + out.Spec.Deletion.NodeDrainTimeoutSeconds = clusterv1.ConvertToSeconds(in.NodeDrainTimeout) + out.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = clusterv1.ConvertToSeconds(in.NodeVolumeDetachTimeout) + out.Spec.Deletion.NodeDeletionTimeoutSeconds = clusterv1.ConvertToSeconds(in.NodeDeletionTimeout) + return nil +} +func Convert_v1beta2_KubeadmControlPlaneMachineTemplate_To_v1beta1_KubeadmControlPlaneMachineTemplate(in *controlplanev1.KubeadmControlPlaneMachineTemplate, out *KubeadmControlPlaneMachineTemplate, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_KubeadmControlPlaneMachineTemplate_To_v1beta1_KubeadmControlPlaneMachineTemplate(in, out, s); err != nil { + return err + } + if in.Spec.ReadinessGates != nil { + in, out := &in.Spec.ReadinessGates, &out.ReadinessGates + *out = make([]clusterv1beta1.MachineReadinessGate, len(*in)) + for i := range *in { + if err := clusterv1beta1.Convert_v1beta2_MachineReadinessGate_To_v1beta1_MachineReadinessGate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } + out.NodeDrainTimeout = clusterv1.ConvertFromSeconds(in.Spec.Deletion.NodeDrainTimeoutSeconds) + out.NodeVolumeDetachTimeout = clusterv1.ConvertFromSeconds(in.Spec.Deletion.NodeVolumeDetachTimeoutSeconds) + out.NodeDeletionTimeout = clusterv1.ConvertFromSeconds(in.Spec.Deletion.NodeDeletionTimeoutSeconds) + return nil +} + +func Convert_v1beta1_KubeadmControlPlaneTemplateMachineTemplate_To_v1beta2_KubeadmControlPlaneTemplateMachineTemplate(in *KubeadmControlPlaneTemplateMachineTemplate, out *controlplanev1.KubeadmControlPlaneTemplateMachineTemplate, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_KubeadmControlPlaneTemplateMachineTemplate_To_v1beta2_KubeadmControlPlaneTemplateMachineTemplate(in, out, s); err != nil { + return err + } + out.Spec.Deletion.NodeDrainTimeoutSeconds = clusterv1.ConvertToSeconds(in.NodeDrainTimeout) + out.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = clusterv1.ConvertToSeconds(in.NodeVolumeDetachTimeout) + out.Spec.Deletion.NodeDeletionTimeoutSeconds = clusterv1.ConvertToSeconds(in.NodeDeletionTimeout) + return nil +} + +func Convert_v1beta2_KubeadmControlPlaneTemplateMachineTemplate_To_v1beta1_KubeadmControlPlaneTemplateMachineTemplate(in *controlplanev1.KubeadmControlPlaneTemplateMachineTemplate, out *KubeadmControlPlaneTemplateMachineTemplate, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_KubeadmControlPlaneTemplateMachineTemplate_To_v1beta1_KubeadmControlPlaneTemplateMachineTemplate(in, out, s); err != nil { + return err + } + out.NodeDrainTimeout = clusterv1.ConvertFromSeconds(in.Spec.Deletion.NodeDrainTimeoutSeconds) + out.NodeVolumeDetachTimeout = clusterv1.ConvertFromSeconds(in.Spec.Deletion.NodeVolumeDetachTimeoutSeconds) + out.NodeDeletionTimeout = clusterv1.ConvertFromSeconds(in.Spec.Deletion.NodeDeletionTimeoutSeconds) + return nil +} + +func Convert_v1beta1_RemediationStrategy_To_v1beta2_KubeadmControlPlaneRemediationSpec(in *RemediationStrategy, out *controlplanev1.KubeadmControlPlaneRemediationSpec, _ apimachineryconversion.Scope) error { + out.MaxRetry = in.MaxRetry + out.MinHealthyPeriodSeconds = clusterv1.ConvertToSeconds(in.MinHealthyPeriod) + out.RetryPeriodSeconds = clusterv1.ConvertToSeconds(&in.RetryPeriod) + return nil +} + +func Convert_v1beta2_KubeadmControlPlaneRemediationSpec_To_v1beta1_RemediationStrategy(in *controlplanev1.KubeadmControlPlaneRemediationSpec, out *RemediationStrategy, _ apimachineryconversion.Scope) error { + out.MaxRetry = in.MaxRetry + out.MinHealthyPeriod = clusterv1.ConvertFromSeconds(in.MinHealthyPeriodSeconds) + out.RetryPeriod = ptr.Deref(clusterv1.ConvertFromSeconds(in.RetryPeriodSeconds), metav1.Duration{}) + return nil +} + +// Implement local conversion func because conversion-gen is not aware of conversion func in other packages (see https://github.com/kubernetes/code-generator/issues/94) + +func Convert_v1beta1_ObjectMeta_To_v1beta2_ObjectMeta(in *clusterv1beta1.ObjectMeta, out *clusterv1.ObjectMeta, s apimachineryconversion.Scope) error { + return clusterv1beta1.Convert_v1beta1_ObjectMeta_To_v1beta2_ObjectMeta(in, out, s) +} + +func Convert_v1beta2_ObjectMeta_To_v1beta1_ObjectMeta(in *clusterv1.ObjectMeta, out *clusterv1beta1.ObjectMeta, s apimachineryconversion.Scope) error { + return clusterv1beta1.Convert_v1beta2_ObjectMeta_To_v1beta1_ObjectMeta(in, out, s) +} + +func Convert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(in *bootstrapv1beta1.KubeadmConfigSpec, out *bootstrapv1.KubeadmConfigSpec, s apimachineryconversion.Scope) error { + return bootstrapv1beta1.Convert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(in, out, s) +} + +func Convert_v1beta2_KubeadmConfigSpec_To_v1beta1_KubeadmConfigSpec(in *bootstrapv1.KubeadmConfigSpec, out *bootstrapv1beta1.KubeadmConfigSpec, s apimachineryconversion.Scope) error { + return bootstrapv1beta1.Convert_v1beta2_KubeadmConfigSpec_To_v1beta1_KubeadmConfigSpec(in, out, s) +} + +func Convert_v1_Condition_To_v1beta1_Condition(in *metav1.Condition, out *clusterv1beta1.Condition, s apimachineryconversion.Scope) error { + return clusterv1beta1.Convert_v1_Condition_To_v1beta1_Condition(in, out, s) +} + +func Convert_v1beta1_Condition_To_v1_Condition(in *clusterv1beta1.Condition, out *metav1.Condition, s apimachineryconversion.Scope) error { + return clusterv1beta1.Convert_v1beta1_Condition_To_v1_Condition(in, out, s) +} + +func Convert_v1_ObjectReference_To_v1beta2_ContractVersionedObjectReference(_ *corev1.ObjectReference, _ *clusterv1.ContractVersionedObjectReference, _ apimachineryconversion.Scope) error { + // This is implemented in ConvertTo/ConvertFrom as we have all necessary information available there. + return nil +} + +func Convert_v1beta2_ContractVersionedObjectReference_To_v1_ObjectReference(_ *clusterv1.ContractVersionedObjectReference, _ *corev1.ObjectReference, _ apimachineryconversion.Scope) error { + // This is implemented in ConvertTo/ConvertFrom as we have all necessary information available there. + return nil +} + +func Convert_v1beta1_LastRemediationStatus_To_v1beta2_LastRemediationStatus(in *LastRemediationStatus, out *controlplanev1.LastRemediationStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta1_LastRemediationStatus_To_v1beta2_LastRemediationStatus(in, out, s); err != nil { + return err + } + out.Time = in.Timestamp + return nil +} + +func Convert_v1beta2_LastRemediationStatus_To_v1beta1_LastRemediationStatus(in *controlplanev1.LastRemediationStatus, out *LastRemediationStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_LastRemediationStatus_To_v1beta1_LastRemediationStatus(in, out, s); err != nil { + return err + } + out.Timestamp = in.Time + return nil +} + +func convertToContractVersionedObjectReference(ref *corev1.ObjectReference) (*clusterv1.ContractVersionedObjectReference, error) { + var apiGroup string + if ref.APIVersion != "" { + gv, err := schema.ParseGroupVersion(ref.APIVersion) + if err != nil { + return nil, fmt.Errorf("failed to convert object: failed to parse apiVersion: %v", err) + } + apiGroup = gv.Group + } + return &clusterv1.ContractVersionedObjectReference{ + APIGroup: apiGroup, + Kind: ref.Kind, + Name: ref.Name, + }, nil +} + +func convertToObjectReference(ref *clusterv1.ContractVersionedObjectReference, namespace string) (*corev1.ObjectReference, error) { + apiVersion, err := apiVersionGetter(schema.GroupKind{ + Group: ref.APIGroup, + Kind: ref.Kind, + }) + if err != nil { + return nil, fmt.Errorf("failed to convert object: %v", err) + } + return &corev1.ObjectReference{ + APIVersion: apiVersion, + Kind: ref.Kind, + Namespace: namespace, + Name: ref.Name, + }, nil +} + +func dropEmptyStringsKubeadmConfigSpec(dst *bootstrapv1beta1.KubeadmConfigSpec) { + for i, u := range dst.Users { + dropEmptyString(&u.Gecos) + dropEmptyString(&u.Groups) + dropEmptyString(&u.HomeDir) + dropEmptyString(&u.Shell) + dropEmptyString(&u.Passwd) + dropEmptyString(&u.PrimaryGroup) + dropEmptyString(&u.Sudo) + dst.Users[i] = u + } + + if dst.DiskSetup != nil { + for i, p := range dst.DiskSetup.Partitions { + dropEmptyString(&p.TableType) + dst.DiskSetup.Partitions[i] = p + } + for i, f := range dst.DiskSetup.Filesystems { + dropEmptyString(&f.Partition) + dropEmptyString(&f.ReplaceFS) + dst.DiskSetup.Filesystems[i] = f + } + } +} + +func dropEmptyStringsKubeadmControlPlaneStatus(dst *KubeadmControlPlaneStatus) { + dropEmptyString(&dst.Version) +} + +func dropEmptyString(s **string) { + if *s != nil && **s == "" { + *s = nil + } +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/webhooks/doc.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/doc.go similarity index 56% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/webhooks/doc.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/doc.go index ad3cd005e8..558f888894 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/webhooks/doc.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2023 The Kubernetes Authors. +Copyright 2021 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. @@ -14,5 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package webhooks contains external webhook implementations for some of our API types. -package webhooks +// Package v1beta1 contains API Schema definitions for the kubeadm v1beta1 API group, +// +k8s:conversion-gen=sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2 +// +kubebuilder:object:generate=true +// +groupName=controlplane.cluster.x-k8s.io +// +// Deprecated: This package is deprecated and is going to be removed when support for v1beta1 will be dropped. +package v1beta1 diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/groupversion_info.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/groupversion_info.go new file mode 100644 index 0000000000..cb9da48056 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/groupversion_info.go @@ -0,0 +1,45 @@ +/* +Copyright 2021 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 v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + // GroupVersion is group version used to register these objects. + GroupVersion = schema.GroupVersion{Group: "controlplane.cluster.x-k8s.io", Version: "v1beta1"} + + // schemeBuilder is used to add go types to the GroupVersionKind scheme. + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = schemeBuilder.AddToScheme + + // localSchemeBuilder is used for type conversions. + localSchemeBuilder = &schemeBuilder + + objectTypes = []runtime.Object{} +) + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(GroupVersion, objectTypes...) + metav1.AddToGroupVersion(scheme, GroupVersion) + return nil +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/kubeadm_control_plane_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/kubeadm_control_plane_types.go new file mode 100644 index 0000000000..b8dd22f194 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/kubeadm_control_plane_types.go @@ -0,0 +1,497 @@ +/* +Copyright 2021 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 v1beta1 + +import ( + "time" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + bootstrapv1beta1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1" + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" + "sigs.k8s.io/cluster-api/errors" +) + +// RolloutStrategyType defines the rollout strategies for a KubeadmControlPlane. +// +kubebuilder:validation:Enum=RollingUpdate +type RolloutStrategyType string + +const ( + // RollingUpdateStrategyType replaces the old control planes by new one using rolling update + // i.e. gradually scale up or down the old control planes and scale up or down the new one. + RollingUpdateStrategyType RolloutStrategyType = "RollingUpdate" +) + +const ( + // KubeadmControlPlaneFinalizer is the finalizer applied to KubeadmControlPlane resources + // by its managing controller. + KubeadmControlPlaneFinalizer = "kubeadm.controlplane.cluster.x-k8s.io" + + // SkipCoreDNSAnnotation annotation explicitly skips reconciling CoreDNS if set. + SkipCoreDNSAnnotation = "controlplane.cluster.x-k8s.io/skip-coredns" + + // SkipKubeProxyAnnotation annotation explicitly skips reconciling kube-proxy if set. + SkipKubeProxyAnnotation = "controlplane.cluster.x-k8s.io/skip-kube-proxy" + + // KubeadmClusterConfigurationAnnotation is a machine annotation that stores the json-marshalled string of KCP ClusterConfiguration. + // This annotation is used to detect any changes in ClusterConfiguration and trigger machine rollout in KCP. + KubeadmClusterConfigurationAnnotation = "controlplane.cluster.x-k8s.io/kubeadm-cluster-configuration" + + // RemediationInProgressAnnotation is used to keep track that a KCP remediation is in progress, and more + // specifically it tracks that the system is in between having deleted an unhealthy machine and recreating its replacement. + // NOTE: if something external to CAPI removes this annotation the system cannot detect the above situation; this can lead to + // failures in updating remediation retry or remediation count (both counters restart from zero). + RemediationInProgressAnnotation = "controlplane.cluster.x-k8s.io/remediation-in-progress" + + // RemediationForAnnotation is used to link a new machine to the unhealthy machine it is replacing; + // please note that in case of retry, when also the remediating machine fails, the system keeps track of + // the first machine of the sequence only. + // NOTE: if something external to CAPI removes this annotation the system this can lead to + // failures in updating remediation retry (the counter restarts from zero). + RemediationForAnnotation = "controlplane.cluster.x-k8s.io/remediation-for" + + // PreTerminateHookCleanupAnnotation is the annotation KCP sets on Machines to ensure it can later remove the + // etcd member right before Machine termination (i.e. before InfraMachine deletion). + // Note: Starting with Kubernetes v1.31 this hook will wait for all other pre-terminate hooks to finish to + // ensure it runs last (thus ensuring that kubelet is still working while other pre-terminate hooks run). + PreTerminateHookCleanupAnnotation = clusterv1beta1.PreTerminateDeleteHookAnnotationPrefix + "/kcp-cleanup" + + // DefaultMinHealthyPeriod defines the default minimum period before we consider a remediation on a + // machine unrelated from the previous remediation. + DefaultMinHealthyPeriod = 1 * time.Hour +) + +// KubeadmControlPlaneSpec defines the desired state of KubeadmControlPlane. +type KubeadmControlPlaneSpec struct { + // replicas is the number of desired machines. Defaults to 1. When stacked etcd is used only + // odd numbers are permitted, as per [etcd best practice](https://etcd.io/docs/v3.3.12/faq/#why-an-odd-number-of-cluster-members). + // This is a pointer to distinguish between explicit zero and not specified. + // +optional + Replicas *int32 `json:"replicas,omitempty"` + + // version defines the desired Kubernetes version. + // Please note that if kubeadmConfigSpec.ClusterConfiguration.imageRepository is not set + // we don't allow upgrades to versions >= v1.22.0 for which kubeadm uses the old registry (k8s.gcr.io). + // Please use a newer patch version with the new registry instead. The default registries of kubeadm are: + // * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0 + // * k8s.gcr.io (old registry): all older versions + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Version string `json:"version"` + + // machineTemplate contains information about how machines + // should be shaped when creating or updating a control plane. + // +required + MachineTemplate KubeadmControlPlaneMachineTemplate `json:"machineTemplate"` + + // kubeadmConfigSpec is a KubeadmConfigSpec + // to use for initializing and joining machines to the control plane. + // +required + KubeadmConfigSpec bootstrapv1beta1.KubeadmConfigSpec `json:"kubeadmConfigSpec"` + + // rolloutBefore is a field to indicate a rollout should be performed + // if the specified criteria is met. + // +optional + RolloutBefore *RolloutBefore `json:"rolloutBefore,omitempty"` + + // rolloutAfter is a field to indicate a rollout should be performed + // after the specified time even if no changes have been made to the + // KubeadmControlPlane. + // Example: In the YAML the time can be specified in the RFC3339 format. + // To specify the rolloutAfter target as March 9, 2023, at 9 am UTC + // use "2023-03-09T09:00:00Z". + // +optional + RolloutAfter *metav1.Time `json:"rolloutAfter,omitempty"` + + // rolloutStrategy is the RolloutStrategy to use to replace control plane machines with + // new ones. + // +optional + // +kubebuilder:default={type: "RollingUpdate", rollingUpdate: {maxSurge: 1}} + RolloutStrategy *RolloutStrategy `json:"rolloutStrategy,omitempty"` + + // remediationStrategy is the RemediationStrategy that controls how control plane machine remediation happens. + // +optional + RemediationStrategy *RemediationStrategy `json:"remediationStrategy,omitempty"` + + // machineNamingStrategy allows changing the naming pattern used when creating Machines. + // InfraMachines & KubeadmConfigs will use the same name as the corresponding Machines. + // +optional + MachineNamingStrategy *MachineNamingStrategy `json:"machineNamingStrategy,omitempty"` +} + +// KubeadmControlPlaneMachineTemplate defines the template for Machines +// in a KubeadmControlPlane object. +type KubeadmControlPlaneMachineTemplate struct { + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + ObjectMeta clusterv1beta1.ObjectMeta `json:"metadata,omitempty"` + + // infrastructureRef is a required reference to a custom resource + // offered by an infrastructure provider. + // +required + InfrastructureRef corev1.ObjectReference `json:"infrastructureRef"` + + // readinessGates specifies additional conditions to include when evaluating Machine Ready condition; + // KubeadmControlPlane will always add readinessGates for the condition it is setting on the Machine: + // APIServerPodHealthy, SchedulerPodHealthy, ControllerManagerPodHealthy, and if etcd is managed by CKP also + // EtcdPodHealthy, EtcdMemberHealthy. + // + // This field can be used e.g. to instruct the machine controller to include in the computation for Machine's ready + // computation a condition, managed by an external controllers, reporting the status of special software/hardware installed on the Machine. + // + // NOTE: This field is considered only for computing v1beta2 conditions. + // +optional + // +listType=map + // +listMapKey=conditionType + // +kubebuilder:validation:MaxItems=32 + ReadinessGates []clusterv1beta1.MachineReadinessGate `json:"readinessGates,omitempty"` + + // nodeDrainTimeout is the total amount of time that the controller will spend on draining a controlplane node + // The default value is 0, meaning that the node can be drained without any time limitations. + // NOTE: NodeDrainTimeout is different from `kubectl drain --timeout` + // +optional + NodeDrainTimeout *metav1.Duration `json:"nodeDrainTimeout,omitempty"` + + // nodeVolumeDetachTimeout is the total amount of time that the controller will spend on waiting for all volumes + // to be detached. The default value is 0, meaning that the volumes can be detached without any time limitations. + // +optional + NodeVolumeDetachTimeout *metav1.Duration `json:"nodeVolumeDetachTimeout,omitempty"` + + // nodeDeletionTimeout defines how long the machine controller will attempt to delete the Node that the Machine + // hosts after the Machine is marked for deletion. A duration of 0 will retry deletion indefinitely. + // If no value is provided, the default value for this property of the Machine resource will be used. + // +optional + NodeDeletionTimeout *metav1.Duration `json:"nodeDeletionTimeout,omitempty"` +} + +// RolloutBefore describes when a rollout should be performed on the KCP machines. +type RolloutBefore struct { + // certificatesExpiryDays indicates a rollout needs to be performed if the + // certificates of the machine will expire within the specified days. + // +optional + CertificatesExpiryDays *int32 `json:"certificatesExpiryDays,omitempty"` +} + +// RolloutStrategy describes how to replace existing machines +// with new ones. +type RolloutStrategy struct { + // type of rollout. Currently the only supported strategy is + // "RollingUpdate". + // Default is RollingUpdate. + // +optional + Type RolloutStrategyType `json:"type,omitempty"` + + // rollingUpdate is the rolling update config params. Present only if + // RolloutStrategyType = RollingUpdate. + // +optional + RollingUpdate *RollingUpdate `json:"rollingUpdate,omitempty"` +} + +// RollingUpdate is used to control the desired behavior of rolling update. +type RollingUpdate struct { + // maxSurge is the maximum number of control planes that can be scheduled above or under the + // desired number of control planes. + // Value can be an absolute number 1 or 0. + // Defaults to 1. + // Example: when this is set to 1, the control plane can be scaled + // up immediately when the rolling update starts. + // +optional + MaxSurge *intstr.IntOrString `json:"maxSurge,omitempty"` +} + +// RemediationStrategy allows to define how control plane machine remediation happens. +type RemediationStrategy struct { + // maxRetry is the Max number of retries while attempting to remediate an unhealthy machine. + // A retry happens when a machine that was created as a replacement for an unhealthy machine also fails. + // For example, given a control plane with three machines M1, M2, M3: + // + // M1 become unhealthy; remediation happens, and M1-1 is created as a replacement. + // If M1-1 (replacement of M1) has problems while bootstrapping it will become unhealthy, and then be + // remediated; such operation is considered a retry, remediation-retry #1. + // If M1-2 (replacement of M1-1) becomes unhealthy, remediation-retry #2 will happen, etc. + // + // A retry could happen only after RetryPeriod from the previous retry. + // If a machine is marked as unhealthy after MinHealthyPeriod from the previous remediation expired, + // this is not considered a retry anymore because the new issue is assumed unrelated from the previous one. + // + // If not set, the remedation will be retried infinitely. + // +optional + MaxRetry *int32 `json:"maxRetry,omitempty"` + + // retryPeriod is the duration that KCP should wait before remediating a machine being created as a replacement + // for an unhealthy machine (a retry). + // + // If not set, a retry will happen immediately. + // +optional + RetryPeriod metav1.Duration `json:"retryPeriod,omitempty"` + + // minHealthyPeriod defines the duration after which KCP will consider any failure to a machine unrelated + // from the previous one. In this case the remediation is not considered a retry anymore, and thus the retry + // counter restarts from 0. For example, assuming MinHealthyPeriod is set to 1h (default) + // + // M1 become unhealthy; remediation happens, and M1-1 is created as a replacement. + // If M1-1 (replacement of M1) has problems within the 1hr after the creation, also + // this machine will be remediated and this operation is considered a retry - a problem related + // to the original issue happened to M1 -. + // + // If instead the problem on M1-1 is happening after MinHealthyPeriod expired, e.g. four days after + // m1-1 has been created as a remediation of M1, the problem on M1-1 is considered unrelated to + // the original issue happened to M1. + // + // If not set, this value is defaulted to 1h. + // +optional + MinHealthyPeriod *metav1.Duration `json:"minHealthyPeriod,omitempty"` +} + +// MachineNamingStrategy allows changing the naming pattern used when creating Machines. +// InfraMachines & KubeadmConfigs will use the same name as the corresponding Machines. +type MachineNamingStrategy struct { + // template defines the template to use for generating the names of the Machine objects. + // If not defined, it will fallback to `{{ .kubeadmControlPlane.name }}-{{ .random }}`. + // If the generated name string exceeds 63 characters, it will be trimmed to 58 characters and will + // get concatenated with a random suffix of length 5. + // Length of the template string must not exceed 256 characters. + // The template allows the following variables `.cluster.name`, `.kubeadmControlPlane.name` and `.random`. + // The variable `.cluster.name` retrieves the name of the cluster object that owns the Machines being created. + // The variable `.kubeadmControlPlane.name` retrieves the name of the KubeadmControlPlane object that owns the Machines being created. + // The variable `.random` is substituted with random alphanumeric string, without vowels, of length 5. This variable is required + // part of the template. If not provided, validation will fail. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Template string `json:"template,omitempty"` +} + +// KubeadmControlPlaneStatus defines the observed state of KubeadmControlPlane. +type KubeadmControlPlaneStatus struct { + // selector is the label selector in string format to avoid introspection + // by clients, and is used to provide the CRD-based integration for the + // scale subresource and additional integrations for things like kubectl + // describe.. The string will be in the same format as the query-param syntax. + // More info about label selectors: http://kubernetes.io/docs/user-guide/labels#label-selectors + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=4096 + Selector string `json:"selector,omitempty"` + + // replicas is the total number of non-terminated machines targeted by this control plane + // (their labels match the selector). + // +optional + Replicas int32 `json:"replicas"` + + // version represents the minimum Kubernetes version for the control plane machines + // in the cluster. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Version *string `json:"version,omitempty"` + + // updatedReplicas is the total number of non-terminated machines targeted by this control plane + // that have the desired template spec. + // +optional + UpdatedReplicas int32 `json:"updatedReplicas"` + + // readyReplicas is the total number of fully running and ready control plane machines. + // +optional + ReadyReplicas int32 `json:"readyReplicas"` + + // unavailableReplicas is the total number of unavailable machines targeted by this control plane. + // This is the total number of machines that are still required for + // the deployment to have 100% available capacity. They may either + // be machines that are running but not yet ready or machines + // that still have not been created. + // + // Deprecated: This field is deprecated and is going to be removed when support for v1beta1 will be dropped. Please see https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more details. + // + // +optional + UnavailableReplicas int32 `json:"unavailableReplicas"` + + // initialized denotes that the KubeadmControlPlane API Server is initialized and thus + // it can accept requests. + // NOTE: this field is part of the Cluster API contract and it is used to orchestrate provisioning. + // The value of this field is never updated after provisioning is completed. Please use conditions + // to check the operational state of the control plane. + // +optional + Initialized bool `json:"initialized"` + + // ready denotes that the KubeadmControlPlane API Server became ready during initial provisioning + // to receive requests. + // NOTE: this field is part of the Cluster API contract and it is used to orchestrate provisioning. + // The value of this field is never updated after provisioning is completed. Please use conditions + // to check the operational state of the control plane. + // +optional + Ready bool `json:"ready"` + + // failureReason indicates that there is a terminal problem reconciling the + // state, and will be set to a token value suitable for + // programmatic interpretation. + // + // Deprecated: This field is deprecated and is going to be removed when support for v1beta1 will be dropped. Please see https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more details. + // + // +optional + FailureReason errors.KubeadmControlPlaneStatusError `json:"failureReason,omitempty"` + + // failureMessage indicates that there is a terminal problem reconciling the + // state, and will be set to a descriptive error message. + // + // Deprecated: This field is deprecated and is going to be removed when support for v1beta1 will be dropped. Please see https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more details. + // + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=10240 + FailureMessage *string `json:"failureMessage,omitempty"` + + // observedGeneration is the latest generation observed by the controller. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // conditions defines current service state of the KubeadmControlPlane. + // +optional + Conditions clusterv1beta1.Conditions `json:"conditions,omitempty"` + + // lastRemediation stores info about last remediation performed. + // +optional + LastRemediation *LastRemediationStatus `json:"lastRemediation,omitempty"` + + // v1beta2 groups all the fields that will be added or modified in KubeadmControlPlane's status with the V1Beta2 version. + // +optional + V1Beta2 *KubeadmControlPlaneV1Beta2Status `json:"v1beta2,omitempty"` +} + +// KubeadmControlPlaneV1Beta2Status Groups all the fields that will be added or modified in KubeadmControlPlane with the V1Beta2 version. +// See https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more context. +type KubeadmControlPlaneV1Beta2Status struct { + // conditions represents the observations of a KubeadmControlPlane's current state. + // Known condition types are Available, CertificatesAvailable, EtcdClusterAvailable, MachinesReady, MachinesUpToDate, + // ScalingUp, ScalingDown, Remediating, Deleting, Paused. + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=32 + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // readyReplicas is the number of ready replicas for this KubeadmControlPlane. A machine is considered ready when Machine's Ready condition is true. + // +optional + ReadyReplicas *int32 `json:"readyReplicas,omitempty"` + + // availableReplicas is the number of available replicas targeted by this KubeadmControlPlane. A machine is considered available when Machine's Available condition is true. + // +optional + AvailableReplicas *int32 `json:"availableReplicas,omitempty"` + + // upToDateReplicas is the number of up-to-date replicas targeted by this KubeadmControlPlane. A machine is considered up-to-date when Machine's UpToDate condition is true. + // +optional + UpToDateReplicas *int32 `json:"upToDateReplicas,omitempty"` +} + +// LastRemediationStatus stores info about last remediation performed. +// NOTE: if for any reason information about last remediation are lost, RetryCount is going to restart from 0 and thus +// more remediations than expected might happen. +type LastRemediationStatus struct { + // machine is the machine name of the latest machine being remediated. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Machine string `json:"machine"` + + // timestamp is when last remediation happened. It is represented in RFC3339 form and is in UTC. + // +required + Timestamp metav1.Time `json:"timestamp"` + + // retryCount used to keep track of remediation retry for the last remediated machine. + // A retry happens when a machine that was created as a replacement for an unhealthy machine also fails. + // +required + RetryCount int32 `json:"retryCount"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=kubeadmcontrolplanes,shortName=kcp,scope=Namespaced,categories=cluster-api +// +kubebuilder:deprecatedversion +// +kubebuilder:subresource:status +// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.selector +// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels['cluster\\.x-k8s\\.io/cluster-name']",description="Cluster" +// +kubebuilder:printcolumn:name="Initialized",type=boolean,JSONPath=".status.initialized",description="This denotes whether or not the control plane has the uploaded kubeadm-config configmap" +// +kubebuilder:printcolumn:name="API Server Available",type=boolean,JSONPath=".status.ready",description="KubeadmControlPlane API Server is ready to receive requests" +// +kubebuilder:printcolumn:name="Desired",type=integer,JSONPath=".spec.replicas",description="Total number of machines desired by this control plane",priority=10 +// +kubebuilder:printcolumn:name="Replicas",type=integer,JSONPath=".status.replicas",description="Total number of non-terminated machines targeted by this control plane" +// +kubebuilder:printcolumn:name="Ready",type=integer,JSONPath=".status.readyReplicas",description="Total number of fully running and ready control plane machines" +// +kubebuilder:printcolumn:name="Updated",type=integer,JSONPath=".status.updatedReplicas",description="Total number of non-terminated machines targeted by this control plane that have the desired template spec" +// +kubebuilder:printcolumn:name="Unavailable",type=integer,JSONPath=".status.unavailableReplicas",description="Total number of unavailable machines targeted by this control plane" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of KubeadmControlPlane" +// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=".spec.version",description="Kubernetes version associated with this control plane" + +// KubeadmControlPlane is the Schema for the KubeadmControlPlane API. +type KubeadmControlPlane struct { + metav1.TypeMeta `json:",inline"` + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // spec is the desired state of KubeadmControlPlane. + // +optional + Spec KubeadmControlPlaneSpec `json:"spec,omitempty"` + // status is the observed state of KubeadmControlPlane. + // +optional + Status KubeadmControlPlaneStatus `json:"status,omitempty"` +} + +// GetConditions returns the set of conditions for this object. +func (in *KubeadmControlPlane) GetConditions() clusterv1beta1.Conditions { + return in.Status.Conditions +} + +// SetConditions sets the conditions on this object. +func (in *KubeadmControlPlane) SetConditions(conditions clusterv1beta1.Conditions) { + in.Status.Conditions = conditions +} + +// GetV1Beta2Conditions returns the set of conditions for this object. +func (in *KubeadmControlPlane) GetV1Beta2Conditions() []metav1.Condition { + if in.Status.V1Beta2 == nil { + return nil + } + return in.Status.V1Beta2.Conditions +} + +// SetV1Beta2Conditions sets conditions for an API object. +func (in *KubeadmControlPlane) SetV1Beta2Conditions(conditions []metav1.Condition) { + if in.Status.V1Beta2 == nil { + in.Status.V1Beta2 = &KubeadmControlPlaneV1Beta2Status{} + } + in.Status.V1Beta2.Conditions = conditions +} + +// +kubebuilder:object:root=true + +// KubeadmControlPlaneList contains a list of KubeadmControlPlane. +type KubeadmControlPlaneList struct { + metav1.TypeMeta `json:",inline"` + // metadata is the standard list's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#lists-and-simple-kinds + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + // items is the list of KubeadmControlPlanes. + Items []KubeadmControlPlane `json:"items"` +} + +func init() { + objectTypes = append(objectTypes, &KubeadmControlPlane{}, &KubeadmControlPlaneList{}) +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/kubeadmcontrolplanetemplate_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/kubeadmcontrolplanetemplate_types.go new file mode 100644 index 0000000000..81ddd06e4e --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/kubeadmcontrolplanetemplate_types.go @@ -0,0 +1,153 @@ +/* +Copyright 2021 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 v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + bootstrapv1beta1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1" + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" +) + +// KubeadmControlPlaneTemplateSpec defines the desired state of KubeadmControlPlaneTemplate. +type KubeadmControlPlaneTemplateSpec struct { + // template defines the desired state of KubeadmControlPlaneTemplate. + // +required + Template KubeadmControlPlaneTemplateResource `json:"template"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=kubeadmcontrolplanetemplates,scope=Namespaced,categories=cluster-api +// +kubebuilder:deprecatedversion +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of KubeadmControlPlaneTemplate" + +// KubeadmControlPlaneTemplate is the Schema for the kubeadmcontrolplanetemplates API. +type KubeadmControlPlaneTemplate struct { + metav1.TypeMeta `json:",inline"` + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // spec is the desired state of KubeadmControlPlaneTemplate. + // +optional + Spec KubeadmControlPlaneTemplateSpec `json:"spec,omitempty"` +} + +// +kubebuilder:object:root=true + +// KubeadmControlPlaneTemplateList contains a list of KubeadmControlPlaneTemplate. +type KubeadmControlPlaneTemplateList struct { + metav1.TypeMeta `json:",inline"` + // metadata is the standard list's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#lists-and-simple-kinds + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + // items is the list of KubeadmControlPlaneTemplates. + Items []KubeadmControlPlaneTemplate `json:"items"` +} + +func init() { + objectTypes = append(objectTypes, &KubeadmControlPlaneTemplate{}, &KubeadmControlPlaneTemplateList{}) +} + +// KubeadmControlPlaneTemplateResource describes the data needed to create a KubeadmControlPlane from a template. +type KubeadmControlPlaneTemplateResource struct { + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + ObjectMeta clusterv1beta1.ObjectMeta `json:"metadata,omitempty"` + + // spec is the desired state of KubeadmControlPlaneTemplateResource. + // +required + Spec KubeadmControlPlaneTemplateResourceSpec `json:"spec"` +} + +// KubeadmControlPlaneTemplateResourceSpec defines the desired state of KubeadmControlPlane. +// NOTE: KubeadmControlPlaneTemplateResourceSpec is similar to KubeadmControlPlaneSpec but +// omits Replicas and Version fields. These fields do not make sense on the KubeadmControlPlaneTemplate, +// because they are calculated by the Cluster topology reconciler during reconciliation and thus cannot +// be configured on the KubeadmControlPlaneTemplate. +type KubeadmControlPlaneTemplateResourceSpec struct { + // machineTemplate contains information about how machines + // should be shaped when creating or updating a control plane. + // +optional + MachineTemplate *KubeadmControlPlaneTemplateMachineTemplate `json:"machineTemplate,omitempty"` + + // kubeadmConfigSpec is a KubeadmConfigSpec + // to use for initializing and joining machines to the control plane. + // +required + KubeadmConfigSpec bootstrapv1beta1.KubeadmConfigSpec `json:"kubeadmConfigSpec"` + + // rolloutBefore is a field to indicate a rollout should be performed + // if the specified criteria is met. + // + // +optional + RolloutBefore *RolloutBefore `json:"rolloutBefore,omitempty"` + + // rolloutAfter is a field to indicate a rollout should be performed + // after the specified time even if no changes have been made to the + // KubeadmControlPlane. + // + // +optional + RolloutAfter *metav1.Time `json:"rolloutAfter,omitempty"` + + // rolloutStrategy is the RolloutStrategy to use to replace control plane machines with + // new ones. + // +optional + // +kubebuilder:default={type: "RollingUpdate", rollingUpdate: {maxSurge: 1}} + RolloutStrategy *RolloutStrategy `json:"rolloutStrategy,omitempty"` + + // remediationStrategy is the RemediationStrategy that controls how control plane machine remediation happens. + // +optional + RemediationStrategy *RemediationStrategy `json:"remediationStrategy,omitempty"` + + // machineNamingStrategy allows changing the naming pattern used when creating Machines. + // InfraMachines & KubeadmConfigs will use the same name as the corresponding Machines. + // +optional + MachineNamingStrategy *MachineNamingStrategy `json:"machineNamingStrategy,omitempty"` +} + +// KubeadmControlPlaneTemplateMachineTemplate defines the template for Machines +// in a KubeadmControlPlaneTemplate object. +// NOTE: KubeadmControlPlaneTemplateMachineTemplate is similar to KubeadmControlPlaneMachineTemplate but +// omits ObjectMeta and InfrastructureRef fields. These fields do not make sense on the KubeadmControlPlaneTemplate, +// because they are calculated by the Cluster topology reconciler during reconciliation and thus cannot +// be configured on the KubeadmControlPlaneTemplate. +type KubeadmControlPlaneTemplateMachineTemplate struct { + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + ObjectMeta clusterv1beta1.ObjectMeta `json:"metadata,omitempty"` + + // nodeDrainTimeout is the total amount of time that the controller will spend on draining a controlplane node + // The default value is 0, meaning that the node can be drained without any time limitations. + // NOTE: NodeDrainTimeout is different from `kubectl drain --timeout` + // +optional + NodeDrainTimeout *metav1.Duration `json:"nodeDrainTimeout,omitempty"` + + // nodeVolumeDetachTimeout is the total amount of time that the controller will spend on waiting for all volumes + // to be detached. The default value is 0, meaning that the volumes can be detached without any time limitations. + // +optional + NodeVolumeDetachTimeout *metav1.Duration `json:"nodeVolumeDetachTimeout,omitempty"` + + // nodeDeletionTimeout defines how long the machine controller will attempt to delete the Node that the Machine + // hosts after the Machine is marked for deletion. A duration of 0 will retry deletion indefinitely. + // If no value is provided, the default value for this property of the Machine resource will be used. + // +optional + NodeDeletionTimeout *metav1.Duration `json:"nodeDeletionTimeout,omitempty"` +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/v1beta2_condition_consts.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/v1beta2_condition_consts.go new file mode 100644 index 0000000000..3e5ef18916 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/v1beta2_condition_consts.go @@ -0,0 +1,362 @@ +/* +Copyright 2024 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 v1beta1 + +import ( + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" +) + +// KubeadmControlPlane's Available condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmControlPlaneAvailableV1Beta2Condition is true if KubeadmControlPlane is not deleted, `CertificatesAvailable` is true, + // at least one Machine with healthy control plane components, and etcd has enough operational members to meet quorum requirements. + // More specifically, considering how kubeadm layouts components: + // - Kubernetes API server, scheduler and controller manager health is inferred by the status of + // the corresponding Pods hosted on each machine. + // - In case of managed etcd, also a healthy etcd Pod and a healthy etcd member must exist on the same + // machine with the healthy Kubernetes API server, scheduler and controller manager, otherwise the k8s control + // plane cannot be considered operational (if etcd is not operational on a machine, most likely also API server, + // scheduler and controller manager on the same machine will be impacted). + // - In case of external etcd, KCP cannot make any assumption on etcd status, so all the etcd checks are skipped. + // + // Please note that when this condition is true, partial unavailability will be surfaced in the condition message, + // but with a 10s delay to ensure flakes do not impact condition stability. + KubeadmControlPlaneAvailableV1Beta2Condition = clusterv1beta1.AvailableV1Beta2Condition + + // KubeadmControlPlaneAvailableInspectionFailedV1Beta2Reason documents a failure when inspecting the status of the + // etcd cluster hosted on KubeadmControlPlane controlled machines. + KubeadmControlPlaneAvailableInspectionFailedV1Beta2Reason = clusterv1beta1.InspectionFailedV1Beta2Reason + + // KubeadmControlPlaneAvailableV1Beta2Reason surfaces when the KubeadmControlPlane is available. + KubeadmControlPlaneAvailableV1Beta2Reason = clusterv1beta1.AvailableV1Beta2Reason + + // KubeadmControlPlaneNotAvailableV1Beta2Reason surfaces when the KubeadmControlPlane is not available. + KubeadmControlPlaneNotAvailableV1Beta2Reason = clusterv1beta1.NotAvailableV1Beta2Reason +) + +// KubeadmControlPlane's Initialized condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmControlPlaneInitializedV1Beta2Condition is true when the control plane is functional enough to accept + // requests. This information is usually used as a signal for starting all the provisioning operations that + // depend on a functional API server, but do not require a full HA control plane to exist. + KubeadmControlPlaneInitializedV1Beta2Condition = "Initialized" + + // KubeadmControlPlaneInitializedV1Beta2Reason surfaces when the control plane is initialized. + KubeadmControlPlaneInitializedV1Beta2Reason = "Initialized" + + // KubeadmControlPlaneNotInitializedV1Beta2Reason surfaces when the control plane is not initialized. + KubeadmControlPlaneNotInitializedV1Beta2Reason = "NotInitialized" +) + +// KubeadmControlPlane's CertificatesAvailable condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmControlPlaneCertificatesAvailableV1Beta2Condition True if all the cluster certificates exist. + KubeadmControlPlaneCertificatesAvailableV1Beta2Condition = "CertificatesAvailable" + + // KubeadmControlPlaneCertificatesInternalErrorV1Beta2Reason surfaces unexpected failures when reconciling cluster certificates. + KubeadmControlPlaneCertificatesInternalErrorV1Beta2Reason = clusterv1beta1.InternalErrorV1Beta2Reason + + // KubeadmControlPlaneCertificatesAvailableV1Beta2Reason surfaces when cluster certificates are available, + // no matter if those certificates have been provided by the user or generated by KubeadmControlPlane itself. + // Cluster certificates include: certificate authorities for ca, sa, front-proxy, etcd, and if external etcd is used, + // also the apiserver-etcd-client client certificate. + KubeadmControlPlaneCertificatesAvailableV1Beta2Reason = clusterv1beta1.AvailableV1Beta2Reason +) + +// KubeadmControlPlane's EtcdClusterHealthy condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmControlPlaneEtcdClusterHealthyV1Beta2Condition surfaces issues to etcd cluster hosted on machines managed by this object. + // It is computed as aggregation of Machine's EtcdMemberHealthy conditions plus additional checks validating + // potential issues to etcd quorum. + // Note: this condition is not set when using an external etcd. + KubeadmControlPlaneEtcdClusterHealthyV1Beta2Condition = "EtcdClusterHealthy" + + // KubeadmControlPlaneEtcdClusterInspectionFailedV1Beta2Reason documents a failure when inspecting the status of the + // etcd cluster hosted on KubeadmControlPlane controlled machines. + KubeadmControlPlaneEtcdClusterInspectionFailedV1Beta2Reason = clusterv1beta1.InspectionFailedV1Beta2Reason + + // KubeadmControlPlaneEtcdClusterConnectionDownV1Beta2Reason surfaces that the connection to the workload + // cluster is down. + KubeadmControlPlaneEtcdClusterConnectionDownV1Beta2Reason = clusterv1beta1.ConnectionDownV1Beta2Reason + + // KubeadmControlPlaneEtcdClusterHealthyV1Beta2Reason surfaces when the etcd cluster hosted on KubeadmControlPlane + // machines is healthy. + KubeadmControlPlaneEtcdClusterHealthyV1Beta2Reason = "Healthy" + + // KubeadmControlPlaneEtcdClusterNotHealthyV1Beta2Reason surfaces when the etcd cluster hosted on KubeadmControlPlane + // machines is not healthy. + KubeadmControlPlaneEtcdClusterNotHealthyV1Beta2Reason = "NotHealthy" + + // KubeadmControlPlaneEtcdClusterHealthUnknownV1Beta2Reason surfaces when the health status of the etcd cluster hosted + // on KubeadmControlPlane machines is unknown. + KubeadmControlPlaneEtcdClusterHealthUnknownV1Beta2Reason = "HealthUnknown" +) + +// KubeadmControlPlane's ControlPlaneComponentsHealthy condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmControlPlaneControlPlaneComponentsHealthyV1Beta2Condition surfaces issues to Kubernetes control plane components + // hosted on machines managed by this object. It is computed as aggregation of Machine's `APIServerPodHealthy`, + // `ControllerManagerPodHealthy`, `SchedulerPodHealthy`, `EtcdPodHealthy` conditions plus additional checks on + // control plane machines and nodes. + KubeadmControlPlaneControlPlaneComponentsHealthyV1Beta2Condition = "ControlPlaneComponentsHealthy" + + // KubeadmControlPlaneControlPlaneComponentsInspectionFailedV1Beta2Reason documents a failure when inspecting the status of the + // control plane components hosted on KubeadmControlPlane controlled machines. + KubeadmControlPlaneControlPlaneComponentsInspectionFailedV1Beta2Reason = clusterv1beta1.InspectionFailedV1Beta2Reason + + // KubeadmControlPlaneControlPlaneComponentsConnectionDownV1Beta2Reason surfaces that the connection to the workload + // cluster is down. + KubeadmControlPlaneControlPlaneComponentsConnectionDownV1Beta2Reason = clusterv1beta1.ConnectionDownV1Beta2Reason + + // KubeadmControlPlaneControlPlaneComponentsHealthyV1Beta2Reason surfaces when the Kubernetes control plane components + // hosted on KubeadmControlPlane machines are healthy. + KubeadmControlPlaneControlPlaneComponentsHealthyV1Beta2Reason = "Healthy" + + // KubeadmControlPlaneControlPlaneComponentsNotHealthyV1Beta2Reason surfaces when the Kubernetes control plane components + // hosted on KubeadmControlPlane machines are not healthy. + KubeadmControlPlaneControlPlaneComponentsNotHealthyV1Beta2Reason = "NotHealthy" + + // KubeadmControlPlaneControlPlaneComponentsHealthUnknownV1Beta2Reason surfaces when the health status of the + // Kubernetes control plane components hosted on KubeadmControlPlane machines is unknown. + KubeadmControlPlaneControlPlaneComponentsHealthUnknownV1Beta2Reason = "HealthUnknown" +) + +// KubeadmControlPlane's MachinesReady condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmControlPlaneMachinesReadyV1Beta2Condition surfaces detail of issues on the controlled machines, if any. + // Please note this will include also APIServerPodHealthy, ControllerManagerPodHealthy, SchedulerPodHealthy conditions. + // If not using an external etcd also EtcdPodHealthy, EtcdMemberHealthy conditions are included. + KubeadmControlPlaneMachinesReadyV1Beta2Condition = clusterv1beta1.MachinesReadyV1Beta2Condition + + // KubeadmControlPlaneMachinesReadyV1Beta2Reason surfaces when all the controlled machine's Ready conditions are true. + KubeadmControlPlaneMachinesReadyV1Beta2Reason = clusterv1beta1.ReadyV1Beta2Reason + + // KubeadmControlPlaneMachinesNotReadyV1Beta2Reason surfaces when at least one of the controlled machine's Ready conditions is false. + KubeadmControlPlaneMachinesNotReadyV1Beta2Reason = clusterv1beta1.NotReadyV1Beta2Reason + + // KubeadmControlPlaneMachinesReadyUnknownV1Beta2Reason surfaces when at least one of the controlled machine's Ready conditions is unknown + // and no one of the controlled machine's Ready conditions is false. + KubeadmControlPlaneMachinesReadyUnknownV1Beta2Reason = clusterv1beta1.ReadyUnknownV1Beta2Reason + + // KubeadmControlPlaneMachinesReadyNoReplicasV1Beta2Reason surfaces when no machines exist for the KubeadmControlPlane. + KubeadmControlPlaneMachinesReadyNoReplicasV1Beta2Reason = clusterv1beta1.NoReplicasV1Beta2Reason + + // KubeadmControlPlaneMachinesReadyInternalErrorV1Beta2Reason surfaces unexpected failures when computing the MachinesReady condition. + KubeadmControlPlaneMachinesReadyInternalErrorV1Beta2Reason = clusterv1beta1.InternalErrorV1Beta2Reason +) + +// KubeadmControlPlane's MachinesUpToDate condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmControlPlaneMachinesUpToDateV1Beta2Condition surfaces details of controlled machines not up to date, if any. + // Note: New machines are considered 10s after machine creation. This gives time to the machine's owner controller to recognize the new machine and add the UpToDate condition. + KubeadmControlPlaneMachinesUpToDateV1Beta2Condition = clusterv1beta1.MachinesUpToDateV1Beta2Condition + + // KubeadmControlPlaneMachinesUpToDateV1Beta2Reason surfaces when all the controlled machine's UpToDate conditions are true. + KubeadmControlPlaneMachinesUpToDateV1Beta2Reason = clusterv1beta1.UpToDateV1Beta2Reason + + // KubeadmControlPlaneMachinesNotUpToDateV1Beta2Reason surfaces when at least one of the controlled machine's UpToDate conditions is false. + KubeadmControlPlaneMachinesNotUpToDateV1Beta2Reason = clusterv1beta1.NotUpToDateV1Beta2Reason + + // KubeadmControlPlaneMachinesUpToDateUnknownV1Beta2Reason surfaces when at least one of the controlled machine's UpToDate conditions is unknown + // and no one of the controlled machine's UpToDate conditions is false. + KubeadmControlPlaneMachinesUpToDateUnknownV1Beta2Reason = clusterv1beta1.UpToDateUnknownV1Beta2Reason + + // KubeadmControlPlaneMachinesUpToDateNoReplicasV1Beta2Reason surfaces when no machines exist for the KubeadmControlPlane. + KubeadmControlPlaneMachinesUpToDateNoReplicasV1Beta2Reason = clusterv1beta1.NoReplicasV1Beta2Reason + + // KubeadmControlPlaneMachinesUpToDateInternalErrorV1Beta2Reason surfaces unexpected failures when computing the MachinesUpToDate condition. + KubeadmControlPlaneMachinesUpToDateInternalErrorV1Beta2Reason = clusterv1beta1.InternalErrorV1Beta2Reason +) + +// KubeadmControlPlane's RollingOut condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmControlPlaneRollingOutV1Beta2Condition is true if there is at least one machine not up-to-date. + KubeadmControlPlaneRollingOutV1Beta2Condition = clusterv1beta1.RollingOutV1Beta2Condition + + // KubeadmControlPlaneRollingOutV1Beta2Reason surfaces when there is at least one machine not up-to-date. + KubeadmControlPlaneRollingOutV1Beta2Reason = clusterv1beta1.RollingOutV1Beta2Reason + + // KubeadmControlPlaneNotRollingOutV1Beta2Reason surfaces when all the machines are up-to-date. + KubeadmControlPlaneNotRollingOutV1Beta2Reason = clusterv1beta1.NotRollingOutV1Beta2Reason +) + +// KubeadmControlPlane's ScalingUp condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmControlPlaneScalingUpV1Beta2Condition is true if actual replicas < desired replicas. + // Note: In case a KubeadmControlPlane preflight check is preventing scale up, this will surface in the condition message. + KubeadmControlPlaneScalingUpV1Beta2Condition = clusterv1beta1.ScalingUpV1Beta2Condition + + // KubeadmControlPlaneScalingUpV1Beta2Reason surfaces when actual replicas < desired replicas. + KubeadmControlPlaneScalingUpV1Beta2Reason = clusterv1beta1.ScalingUpV1Beta2Reason + + // KubeadmControlPlaneNotScalingUpV1Beta2Reason surfaces when actual replicas >= desired replicas. + KubeadmControlPlaneNotScalingUpV1Beta2Reason = clusterv1beta1.NotScalingUpV1Beta2Reason + + // KubeadmControlPlaneScalingUpWaitingForReplicasSetV1Beta2Reason surfaces when the .spec.replicas + // field of the KubeadmControlPlane is not set. + KubeadmControlPlaneScalingUpWaitingForReplicasSetV1Beta2Reason = clusterv1beta1.WaitingForReplicasSetV1Beta2Reason +) + +// KubeadmControlPlane's ScalingDown condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmControlPlaneScalingDownV1Beta2Condition is true if actual replicas > desired replicas. + // Note: In case a KubeadmControlPlane preflight check is preventing scale down, this will surface in the condition message. + KubeadmControlPlaneScalingDownV1Beta2Condition = clusterv1beta1.ScalingDownV1Beta2Condition + + // KubeadmControlPlaneScalingDownV1Beta2Reason surfaces when actual replicas > desired replicas. + KubeadmControlPlaneScalingDownV1Beta2Reason = clusterv1beta1.ScalingDownV1Beta2Reason + + // KubeadmControlPlaneNotScalingDownV1Beta2Reason surfaces when actual replicas <= desired replicas. + KubeadmControlPlaneNotScalingDownV1Beta2Reason = clusterv1beta1.NotScalingDownV1Beta2Reason + + // KubeadmControlPlaneScalingDownWaitingForReplicasSetV1Beta2Reason surfaces when the .spec.replicas + // field of the KubeadmControlPlane is not set. + KubeadmControlPlaneScalingDownWaitingForReplicasSetV1Beta2Reason = clusterv1beta1.WaitingForReplicasSetV1Beta2Reason +) + +// KubeadmControlPlane's Remediating condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmControlPlaneRemediatingV1Beta2Condition surfaces details about ongoing remediation of the controlled machines, if any. + // Note: KubeadmControlPlane only remediates machines with HealthCheckSucceeded set to false and with the OwnerRemediated condition set to false. + KubeadmControlPlaneRemediatingV1Beta2Condition = clusterv1beta1.RemediatingV1Beta2Condition + + // KubeadmControlPlaneRemediatingV1Beta2Reason surfaces when the KubeadmControlPlane has at least one machine with HealthCheckSucceeded set to false + // and with the OwnerRemediated condition set to false. + KubeadmControlPlaneRemediatingV1Beta2Reason = clusterv1beta1.RemediatingV1Beta2Reason + + // KubeadmControlPlaneNotRemediatingV1Beta2Reason surfaces when the KubeadmControlPlane does not have any machine with HealthCheckSucceeded set to false + // and with the OwnerRemediated condition set to false. + KubeadmControlPlaneNotRemediatingV1Beta2Reason = clusterv1beta1.NotRemediatingV1Beta2Reason + + // KubeadmControlPlaneRemediatingInternalErrorV1Beta2Reason surfaces unexpected failures when computing the Remediating condition. + KubeadmControlPlaneRemediatingInternalErrorV1Beta2Reason = clusterv1beta1.InternalErrorV1Beta2Reason +) + +// Reasons that will be used for the OwnerRemediated condition set by MachineHealthCheck on KubeadmControlPlane controlled machines +// being remediated in v1Beta2 API version. +const ( + // KubeadmControlPlaneMachineRemediationInternalErrorV1Beta2Reason surfaces unexpected failures while remediating a control plane machine. + KubeadmControlPlaneMachineRemediationInternalErrorV1Beta2Reason = clusterv1beta1.InternalErrorV1Beta2Reason + + // KubeadmControlPlaneMachineCannotBeRemediatedV1Beta2Reason surfaces when remediation of a control plane machine can't be started. + KubeadmControlPlaneMachineCannotBeRemediatedV1Beta2Reason = "CannotBeRemediated" + + // KubeadmControlPlaneMachineRemediationDeferredV1Beta2Reason surfaces when remediation of a control plane machine must be deferred. + KubeadmControlPlaneMachineRemediationDeferredV1Beta2Reason = "RemediationDeferred" + + // KubeadmControlPlaneMachineRemediationMachineDeletingV1Beta2Reason surfaces when remediation of a control plane machine + // has been completed by deleting the unhealthy machine. + // Note: After an unhealthy machine is deleted, a new one is created by the KubeadmControlPlaneMachine as part of the + // regular reconcile loop that ensures the correct number of replicas exist; KubeadmControlPlane machine waits for + // the new machine to exists before removing the controlplane.cluster.x-k8s.io/remediation-in-progress annotation. + // This is part of a series of safeguards to ensure that operation are performed sequentially on control plane machines. + KubeadmControlPlaneMachineRemediationMachineDeletingV1Beta2Reason = "MachineDeleting" +) + +// KubeadmControlPlane's Deleting condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // KubeadmControlPlaneDeletingV1Beta2Condition surfaces details about ongoing deletion of the controlled machines. + KubeadmControlPlaneDeletingV1Beta2Condition = clusterv1beta1.DeletingV1Beta2Condition + + // KubeadmControlPlaneNotDeletingV1Beta2Reason surfaces when the KCP is not deleting because the + // DeletionTimestamp is not set. + KubeadmControlPlaneNotDeletingV1Beta2Reason = clusterv1beta1.NotDeletingV1Beta2Reason + + // KubeadmControlPlaneDeletingWaitingForWorkersDeletionV1Beta2Reason surfaces when the KCP deletion + // waits for the workers to be deleted. + KubeadmControlPlaneDeletingWaitingForWorkersDeletionV1Beta2Reason = "WaitingForWorkersDeletion" + + // KubeadmControlPlaneDeletingWaitingForMachineDeletionV1Beta2Reason surfaces when the KCP deletion + // waits for the control plane Machines to be deleted. + KubeadmControlPlaneDeletingWaitingForMachineDeletionV1Beta2Reason = "WaitingForMachineDeletion" + + // KubeadmControlPlaneDeletingDeletionCompletedV1Beta2Reason surfaces when the KCP deletion has been completed. + // This reason is set right after the `kubeadm.controlplane.cluster.x-k8s.io` finalizer is removed. + // This means that the object will go away (i.e. be removed from etcd), except if there are other + // finalizers on the KCP object. + KubeadmControlPlaneDeletingDeletionCompletedV1Beta2Reason = clusterv1beta1.DeletionCompletedV1Beta2Reason + + // KubeadmControlPlaneDeletingInternalErrorV1Beta2Reason surfaces unexpected failures when deleting a KCP object. + KubeadmControlPlaneDeletingInternalErrorV1Beta2Reason = clusterv1beta1.InternalErrorV1Beta2Reason +) + +// APIServerPodHealthy, ControllerManagerPodHealthy, SchedulerPodHealthy and EtcdPodHealthy condition and corresponding +// reasons that will be used for KubeadmControlPlane controlled machines in v1Beta2 API version. +const ( + // KubeadmControlPlaneMachineAPIServerPodHealthyV1Beta2Condition surfaces the status of the API server pod hosted on a KubeadmControlPlane controlled machine. + KubeadmControlPlaneMachineAPIServerPodHealthyV1Beta2Condition = "APIServerPodHealthy" + + // KubeadmControlPlaneMachineControllerManagerPodHealthyV1Beta2Condition surfaces the status of the controller manager pod hosted on a KubeadmControlPlane controlled machine. + KubeadmControlPlaneMachineControllerManagerPodHealthyV1Beta2Condition = "ControllerManagerPodHealthy" + + // KubeadmControlPlaneMachineSchedulerPodHealthyV1Beta2Condition surfaces the status of the scheduler pod hosted on a KubeadmControlPlane controlled machine. + KubeadmControlPlaneMachineSchedulerPodHealthyV1Beta2Condition = "SchedulerPodHealthy" + + // KubeadmControlPlaneMachineEtcdPodHealthyV1Beta2Condition surfaces the status of the etcd pod hosted on a KubeadmControlPlane controlled machine. + KubeadmControlPlaneMachineEtcdPodHealthyV1Beta2Condition = "EtcdPodHealthy" + + // KubeadmControlPlaneMachinePodRunningV1Beta2Reason surfaces a pod hosted on a KubeadmControlPlane controlled machine that is running. + KubeadmControlPlaneMachinePodRunningV1Beta2Reason = "Running" + + // KubeadmControlPlaneMachinePodProvisioningV1Beta2Reason surfaces a pod hosted on a KubeadmControlPlane controlled machine + // waiting to be provisioned i.e., Pod is in "Pending" phase. + KubeadmControlPlaneMachinePodProvisioningV1Beta2Reason = "Provisioning" + + // KubeadmControlPlaneMachinePodDoesNotExistV1Beta2Reason surfaces a when a pod hosted on a KubeadmControlPlane controlled machine + // does not exist. + KubeadmControlPlaneMachinePodDoesNotExistV1Beta2Reason = "DoesNotExist" + + // KubeadmControlPlaneMachinePodFailedV1Beta2Reason surfaces a when a pod hosted on a KubeadmControlPlane controlled machine + // failed during provisioning, e.g. CrashLoopBackOff, ImagePullBackOff or if all the containers in a pod have terminated. + KubeadmControlPlaneMachinePodFailedV1Beta2Reason = "Failed" + + // KubeadmControlPlaneMachinePodInspectionFailedV1Beta2Reason documents a failure when inspecting the status of a + // pod hosted on a KubeadmControlPlane controlled machine. + KubeadmControlPlaneMachinePodInspectionFailedV1Beta2Reason = clusterv1beta1.InspectionFailedV1Beta2Reason + + // KubeadmControlPlaneMachinePodConnectionDownV1Beta2Reason surfaces that the connection to the workload + // cluster is down. + KubeadmControlPlaneMachinePodConnectionDownV1Beta2Reason = clusterv1beta1.ConnectionDownV1Beta2Reason + + // KubeadmControlPlaneMachinePodDeletingV1Beta2Reason surfaces when the machine hosting control plane components + // is being deleted. + KubeadmControlPlaneMachinePodDeletingV1Beta2Reason = "Deleting" +) + +// EtcdMemberHealthy condition and corresponding reasons that will be used for KubeadmControlPlane controlled machines in v1Beta2 API version. +const ( + // KubeadmControlPlaneMachineEtcdMemberHealthyV1Beta2Condition surfaces the status of the etcd member hosted on a KubeadmControlPlane controlled machine. + KubeadmControlPlaneMachineEtcdMemberHealthyV1Beta2Condition = "EtcdMemberHealthy" + + // KubeadmControlPlaneMachineEtcdMemberNotHealthyV1Beta2Reason surfaces when the etcd member hosted on a KubeadmControlPlane controlled machine is not healthy. + KubeadmControlPlaneMachineEtcdMemberNotHealthyV1Beta2Reason = "NotHealthy" + + // KubeadmControlPlaneMachineEtcdMemberHealthyV1Beta2Reason surfaces when the etcd member hosted on a KubeadmControlPlane controlled machine is healthy. + KubeadmControlPlaneMachineEtcdMemberHealthyV1Beta2Reason = "Healthy" + + // KubeadmControlPlaneMachineEtcdMemberInspectionFailedV1Beta2Reason documents a failure when inspecting the status of an + // etcd member hosted on a KubeadmControlPlane controlled machine. + KubeadmControlPlaneMachineEtcdMemberInspectionFailedV1Beta2Reason = clusterv1beta1.InspectionFailedV1Beta2Reason + + // KubeadmControlPlaneMachineEtcdMemberConnectionDownV1Beta2Reason surfaces that the connection to the workload + // cluster is down. + KubeadmControlPlaneMachineEtcdMemberConnectionDownV1Beta2Reason = clusterv1beta1.ConnectionDownV1Beta2Reason + + // KubeadmControlPlaneMachineEtcdMemberDeletingV1Beta2Reason surfaces when the machine hosting an etcd member + // is being deleted. + KubeadmControlPlaneMachineEtcdMemberDeletingV1Beta2Reason = "Deleting" +) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/zz_generated.conversion.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/zz_generated.conversion.go new file mode 100644 index 0000000000..dbdf803a53 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/zz_generated.conversion.go @@ -0,0 +1,589 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by conversion-gen. DO NOT EDIT. + +package v1beta1 + +import ( + unsafe "unsafe" + + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + kubeadmv1beta1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1" + kubeadmv1beta2 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" + v1beta2 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2" + corev1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" + corev1beta2 "sigs.k8s.io/cluster-api/api/core/v1beta2" +) + +func init() { + localSchemeBuilder.Register(RegisterConversions) +} + +// RegisterConversions adds conversion functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*KubeadmControlPlane)(nil), (*v1beta2.KubeadmControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmControlPlane_To_v1beta2_KubeadmControlPlane(a.(*KubeadmControlPlane), b.(*v1beta2.KubeadmControlPlane), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeadmControlPlane)(nil), (*KubeadmControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmControlPlane_To_v1beta1_KubeadmControlPlane(a.(*v1beta2.KubeadmControlPlane), b.(*KubeadmControlPlane), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeadmControlPlaneList)(nil), (*v1beta2.KubeadmControlPlaneList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmControlPlaneList_To_v1beta2_KubeadmControlPlaneList(a.(*KubeadmControlPlaneList), b.(*v1beta2.KubeadmControlPlaneList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeadmControlPlaneList)(nil), (*KubeadmControlPlaneList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmControlPlaneList_To_v1beta1_KubeadmControlPlaneList(a.(*v1beta2.KubeadmControlPlaneList), b.(*KubeadmControlPlaneList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeadmControlPlaneTemplate)(nil), (*v1beta2.KubeadmControlPlaneTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmControlPlaneTemplate_To_v1beta2_KubeadmControlPlaneTemplate(a.(*KubeadmControlPlaneTemplate), b.(*v1beta2.KubeadmControlPlaneTemplate), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeadmControlPlaneTemplate)(nil), (*KubeadmControlPlaneTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmControlPlaneTemplate_To_v1beta1_KubeadmControlPlaneTemplate(a.(*v1beta2.KubeadmControlPlaneTemplate), b.(*KubeadmControlPlaneTemplate), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeadmControlPlaneTemplateList)(nil), (*v1beta2.KubeadmControlPlaneTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmControlPlaneTemplateList_To_v1beta2_KubeadmControlPlaneTemplateList(a.(*KubeadmControlPlaneTemplateList), b.(*v1beta2.KubeadmControlPlaneTemplateList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeadmControlPlaneTemplateList)(nil), (*KubeadmControlPlaneTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmControlPlaneTemplateList_To_v1beta1_KubeadmControlPlaneTemplateList(a.(*v1beta2.KubeadmControlPlaneTemplateList), b.(*KubeadmControlPlaneTemplateList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeadmControlPlaneTemplateResource)(nil), (*v1beta2.KubeadmControlPlaneTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmControlPlaneTemplateResource_To_v1beta2_KubeadmControlPlaneTemplateResource(a.(*KubeadmControlPlaneTemplateResource), b.(*v1beta2.KubeadmControlPlaneTemplateResource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeadmControlPlaneTemplateResource)(nil), (*KubeadmControlPlaneTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmControlPlaneTemplateResource_To_v1beta1_KubeadmControlPlaneTemplateResource(a.(*v1beta2.KubeadmControlPlaneTemplateResource), b.(*KubeadmControlPlaneTemplateResource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*KubeadmControlPlaneTemplateSpec)(nil), (*v1beta2.KubeadmControlPlaneTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmControlPlaneTemplateSpec_To_v1beta2_KubeadmControlPlaneTemplateSpec(a.(*KubeadmControlPlaneTemplateSpec), b.(*v1beta2.KubeadmControlPlaneTemplateSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.KubeadmControlPlaneTemplateSpec)(nil), (*KubeadmControlPlaneTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmControlPlaneTemplateSpec_To_v1beta1_KubeadmControlPlaneTemplateSpec(a.(*v1beta2.KubeadmControlPlaneTemplateSpec), b.(*KubeadmControlPlaneTemplateSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1.Condition)(nil), (*corev1beta1.Condition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_Condition_To_v1beta1_Condition(a.(*v1.Condition), b.(*corev1beta1.Condition), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*corev1.ObjectReference)(nil), (*corev1beta2.ContractVersionedObjectReference)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_ObjectReference_To_v1beta2_ContractVersionedObjectReference(a.(*corev1.ObjectReference), b.(*corev1beta2.ContractVersionedObjectReference), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*corev1beta1.Condition)(nil), (*v1.Condition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Condition_To_v1_Condition(a.(*corev1beta1.Condition), b.(*v1.Condition), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*kubeadmv1beta1.KubeadmConfigSpec)(nil), (*kubeadmv1beta2.KubeadmConfigSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(a.(*kubeadmv1beta1.KubeadmConfigSpec), b.(*kubeadmv1beta2.KubeadmConfigSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*KubeadmControlPlaneMachineTemplate)(nil), (*v1beta2.KubeadmControlPlaneMachineTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmControlPlaneMachineTemplate_To_v1beta2_KubeadmControlPlaneMachineTemplate(a.(*KubeadmControlPlaneMachineTemplate), b.(*v1beta2.KubeadmControlPlaneMachineTemplate), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*KubeadmControlPlaneSpec)(nil), (*v1beta2.KubeadmControlPlaneSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmControlPlaneSpec_To_v1beta2_KubeadmControlPlaneSpec(a.(*KubeadmControlPlaneSpec), b.(*v1beta2.KubeadmControlPlaneSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*KubeadmControlPlaneStatus)(nil), (*v1beta2.KubeadmControlPlaneStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmControlPlaneStatus_To_v1beta2_KubeadmControlPlaneStatus(a.(*KubeadmControlPlaneStatus), b.(*v1beta2.KubeadmControlPlaneStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*KubeadmControlPlaneTemplateMachineTemplate)(nil), (*v1beta2.KubeadmControlPlaneTemplateMachineTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmControlPlaneTemplateMachineTemplate_To_v1beta2_KubeadmControlPlaneTemplateMachineTemplate(a.(*KubeadmControlPlaneTemplateMachineTemplate), b.(*v1beta2.KubeadmControlPlaneTemplateMachineTemplate), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*KubeadmControlPlaneTemplateResourceSpec)(nil), (*v1beta2.KubeadmControlPlaneTemplateResourceSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_KubeadmControlPlaneTemplateResourceSpec_To_v1beta2_KubeadmControlPlaneTemplateResourceSpec(a.(*KubeadmControlPlaneTemplateResourceSpec), b.(*v1beta2.KubeadmControlPlaneTemplateResourceSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*LastRemediationStatus)(nil), (*v1beta2.LastRemediationStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_LastRemediationStatus_To_v1beta2_LastRemediationStatus(a.(*LastRemediationStatus), b.(*v1beta2.LastRemediationStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*corev1beta1.ObjectMeta)(nil), (*corev1beta2.ObjectMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ObjectMeta_To_v1beta2_ObjectMeta(a.(*corev1beta1.ObjectMeta), b.(*corev1beta2.ObjectMeta), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*RemediationStrategy)(nil), (*v1beta2.KubeadmControlPlaneRemediationSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_RemediationStrategy_To_v1beta2_KubeadmControlPlaneRemediationSpec(a.(*RemediationStrategy), b.(*v1beta2.KubeadmControlPlaneRemediationSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*corev1beta2.ContractVersionedObjectReference)(nil), (*corev1.ObjectReference)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_ContractVersionedObjectReference_To_v1_ObjectReference(a.(*corev1beta2.ContractVersionedObjectReference), b.(*corev1.ObjectReference), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*kubeadmv1beta2.KubeadmConfigSpec)(nil), (*kubeadmv1beta1.KubeadmConfigSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmConfigSpec_To_v1beta1_KubeadmConfigSpec(a.(*kubeadmv1beta2.KubeadmConfigSpec), b.(*kubeadmv1beta1.KubeadmConfigSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.KubeadmControlPlaneMachineTemplate)(nil), (*KubeadmControlPlaneMachineTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmControlPlaneMachineTemplate_To_v1beta1_KubeadmControlPlaneMachineTemplate(a.(*v1beta2.KubeadmControlPlaneMachineTemplate), b.(*KubeadmControlPlaneMachineTemplate), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.KubeadmControlPlaneRemediationSpec)(nil), (*RemediationStrategy)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmControlPlaneRemediationSpec_To_v1beta1_RemediationStrategy(a.(*v1beta2.KubeadmControlPlaneRemediationSpec), b.(*RemediationStrategy), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.KubeadmControlPlaneSpec)(nil), (*KubeadmControlPlaneSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmControlPlaneSpec_To_v1beta1_KubeadmControlPlaneSpec(a.(*v1beta2.KubeadmControlPlaneSpec), b.(*KubeadmControlPlaneSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.KubeadmControlPlaneStatus)(nil), (*KubeadmControlPlaneStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmControlPlaneStatus_To_v1beta1_KubeadmControlPlaneStatus(a.(*v1beta2.KubeadmControlPlaneStatus), b.(*KubeadmControlPlaneStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.KubeadmControlPlaneTemplateMachineTemplate)(nil), (*KubeadmControlPlaneTemplateMachineTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmControlPlaneTemplateMachineTemplate_To_v1beta1_KubeadmControlPlaneTemplateMachineTemplate(a.(*v1beta2.KubeadmControlPlaneTemplateMachineTemplate), b.(*KubeadmControlPlaneTemplateMachineTemplate), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.KubeadmControlPlaneTemplateResourceSpec)(nil), (*KubeadmControlPlaneTemplateResourceSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_KubeadmControlPlaneTemplateResourceSpec_To_v1beta1_KubeadmControlPlaneTemplateResourceSpec(a.(*v1beta2.KubeadmControlPlaneTemplateResourceSpec), b.(*KubeadmControlPlaneTemplateResourceSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.LastRemediationStatus)(nil), (*LastRemediationStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_LastRemediationStatus_To_v1beta1_LastRemediationStatus(a.(*v1beta2.LastRemediationStatus), b.(*LastRemediationStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*corev1beta2.ObjectMeta)(nil), (*corev1beta1.ObjectMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_ObjectMeta_To_v1beta1_ObjectMeta(a.(*corev1beta2.ObjectMeta), b.(*corev1beta1.ObjectMeta), scope) + }); err != nil { + return err + } + return nil +} + +func autoConvert_v1beta1_KubeadmControlPlane_To_v1beta2_KubeadmControlPlane(in *KubeadmControlPlane, out *v1beta2.KubeadmControlPlane, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta1_KubeadmControlPlaneSpec_To_v1beta2_KubeadmControlPlaneSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta1_KubeadmControlPlaneStatus_To_v1beta2_KubeadmControlPlaneStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_KubeadmControlPlane_To_v1beta2_KubeadmControlPlane is an autogenerated conversion function. +func Convert_v1beta1_KubeadmControlPlane_To_v1beta2_KubeadmControlPlane(in *KubeadmControlPlane, out *v1beta2.KubeadmControlPlane, s conversion.Scope) error { + return autoConvert_v1beta1_KubeadmControlPlane_To_v1beta2_KubeadmControlPlane(in, out, s) +} + +func autoConvert_v1beta2_KubeadmControlPlane_To_v1beta1_KubeadmControlPlane(in *v1beta2.KubeadmControlPlane, out *KubeadmControlPlane, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_KubeadmControlPlaneSpec_To_v1beta1_KubeadmControlPlaneSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_KubeadmControlPlaneStatus_To_v1beta1_KubeadmControlPlaneStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_KubeadmControlPlane_To_v1beta1_KubeadmControlPlane is an autogenerated conversion function. +func Convert_v1beta2_KubeadmControlPlane_To_v1beta1_KubeadmControlPlane(in *v1beta2.KubeadmControlPlane, out *KubeadmControlPlane, s conversion.Scope) error { + return autoConvert_v1beta2_KubeadmControlPlane_To_v1beta1_KubeadmControlPlane(in, out, s) +} + +func autoConvert_v1beta1_KubeadmControlPlaneList_To_v1beta2_KubeadmControlPlaneList(in *KubeadmControlPlaneList, out *v1beta2.KubeadmControlPlaneList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta2.KubeadmControlPlane, len(*in)) + for i := range *in { + if err := Convert_v1beta1_KubeadmControlPlane_To_v1beta2_KubeadmControlPlane(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta1_KubeadmControlPlaneList_To_v1beta2_KubeadmControlPlaneList is an autogenerated conversion function. +func Convert_v1beta1_KubeadmControlPlaneList_To_v1beta2_KubeadmControlPlaneList(in *KubeadmControlPlaneList, out *v1beta2.KubeadmControlPlaneList, s conversion.Scope) error { + return autoConvert_v1beta1_KubeadmControlPlaneList_To_v1beta2_KubeadmControlPlaneList(in, out, s) +} + +func autoConvert_v1beta2_KubeadmControlPlaneList_To_v1beta1_KubeadmControlPlaneList(in *v1beta2.KubeadmControlPlaneList, out *KubeadmControlPlaneList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KubeadmControlPlane, len(*in)) + for i := range *in { + if err := Convert_v1beta2_KubeadmControlPlane_To_v1beta1_KubeadmControlPlane(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_KubeadmControlPlaneList_To_v1beta1_KubeadmControlPlaneList is an autogenerated conversion function. +func Convert_v1beta2_KubeadmControlPlaneList_To_v1beta1_KubeadmControlPlaneList(in *v1beta2.KubeadmControlPlaneList, out *KubeadmControlPlaneList, s conversion.Scope) error { + return autoConvert_v1beta2_KubeadmControlPlaneList_To_v1beta1_KubeadmControlPlaneList(in, out, s) +} + +func autoConvert_v1beta1_KubeadmControlPlaneMachineTemplate_To_v1beta2_KubeadmControlPlaneMachineTemplate(in *KubeadmControlPlaneMachineTemplate, out *v1beta2.KubeadmControlPlaneMachineTemplate, s conversion.Scope) error { + if err := Convert_v1beta1_ObjectMeta_To_v1beta2_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + // WARNING: in.InfrastructureRef requires manual conversion: does not exist in peer-type + // WARNING: in.ReadinessGates requires manual conversion: does not exist in peer-type + // WARNING: in.NodeDrainTimeout requires manual conversion: does not exist in peer-type + // WARNING: in.NodeVolumeDetachTimeout requires manual conversion: does not exist in peer-type + // WARNING: in.NodeDeletionTimeout requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_KubeadmControlPlaneMachineTemplate_To_v1beta1_KubeadmControlPlaneMachineTemplate(in *v1beta2.KubeadmControlPlaneMachineTemplate, out *KubeadmControlPlaneMachineTemplate, s conversion.Scope) error { + if err := Convert_v1beta2_ObjectMeta_To_v1beta1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + // WARNING: in.Spec requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_KubeadmControlPlaneSpec_To_v1beta2_KubeadmControlPlaneSpec(in *KubeadmControlPlaneSpec, out *v1beta2.KubeadmControlPlaneSpec, s conversion.Scope) error { + out.Replicas = (*int32)(unsafe.Pointer(in.Replicas)) + out.Version = in.Version + if err := Convert_v1beta1_KubeadmControlPlaneMachineTemplate_To_v1beta2_KubeadmControlPlaneMachineTemplate(&in.MachineTemplate, &out.MachineTemplate, s); err != nil { + return err + } + if err := Convert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(&in.KubeadmConfigSpec, &out.KubeadmConfigSpec, s); err != nil { + return err + } + // WARNING: in.RolloutBefore requires manual conversion: does not exist in peer-type + // WARNING: in.RolloutAfter requires manual conversion: does not exist in peer-type + // WARNING: in.RolloutStrategy requires manual conversion: does not exist in peer-type + // WARNING: in.RemediationStrategy requires manual conversion: does not exist in peer-type + // WARNING: in.MachineNamingStrategy requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_KubeadmControlPlaneSpec_To_v1beta1_KubeadmControlPlaneSpec(in *v1beta2.KubeadmControlPlaneSpec, out *KubeadmControlPlaneSpec, s conversion.Scope) error { + out.Replicas = (*int32)(unsafe.Pointer(in.Replicas)) + out.Version = in.Version + if err := Convert_v1beta2_KubeadmControlPlaneMachineTemplate_To_v1beta1_KubeadmControlPlaneMachineTemplate(&in.MachineTemplate, &out.MachineTemplate, s); err != nil { + return err + } + if err := Convert_v1beta2_KubeadmConfigSpec_To_v1beta1_KubeadmConfigSpec(&in.KubeadmConfigSpec, &out.KubeadmConfigSpec, s); err != nil { + return err + } + // WARNING: in.Rollout requires manual conversion: does not exist in peer-type + // WARNING: in.Remediation requires manual conversion: does not exist in peer-type + // WARNING: in.MachineNaming requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_KubeadmControlPlaneStatus_To_v1beta2_KubeadmControlPlaneStatus(in *KubeadmControlPlaneStatus, out *v1beta2.KubeadmControlPlaneStatus, s conversion.Scope) error { + out.Selector = in.Selector + if err := v1.Convert_int32_To_Pointer_int32(&in.Replicas, &out.Replicas, s); err != nil { + return err + } + if err := v1.Convert_Pointer_string_To_string(&in.Version, &out.Version, s); err != nil { + return err + } + // WARNING: in.UpdatedReplicas requires manual conversion: does not exist in peer-type + if err := v1.Convert_int32_To_Pointer_int32(&in.ReadyReplicas, &out.ReadyReplicas, s); err != nil { + return err + } + // WARNING: in.UnavailableReplicas requires manual conversion: does not exist in peer-type + // WARNING: in.Initialized requires manual conversion: does not exist in peer-type + // WARNING: in.Ready requires manual conversion: does not exist in peer-type + // WARNING: in.FailureReason requires manual conversion: does not exist in peer-type + // WARNING: in.FailureMessage requires manual conversion: does not exist in peer-type + out.ObservedGeneration = in.ObservedGeneration + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + if err := Convert_v1beta1_Condition_To_v1_Condition(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Conditions = nil + } + // WARNING: in.LastRemediation requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1.LastRemediationStatus vs sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2.LastRemediationStatus) + // WARNING: in.V1Beta2 requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_KubeadmControlPlaneStatus_To_v1beta1_KubeadmControlPlaneStatus(in *v1beta2.KubeadmControlPlaneStatus, out *KubeadmControlPlaneStatus, s conversion.Scope) error { + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(corev1beta1.Conditions, len(*in)) + for i := range *in { + if err := Convert_v1_Condition_To_v1beta1_Condition(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Conditions = nil + } + // WARNING: in.Initialization requires manual conversion: does not exist in peer-type + out.Selector = in.Selector + if err := v1.Convert_Pointer_int32_To_int32(&in.Replicas, &out.Replicas, s); err != nil { + return err + } + if err := v1.Convert_Pointer_int32_To_int32(&in.ReadyReplicas, &out.ReadyReplicas, s); err != nil { + return err + } + // WARNING: in.AvailableReplicas requires manual conversion: does not exist in peer-type + // WARNING: in.UpToDateReplicas requires manual conversion: does not exist in peer-type + if err := v1.Convert_string_To_Pointer_string(&in.Version, &out.Version, s); err != nil { + return err + } + out.ObservedGeneration = in.ObservedGeneration + // WARNING: in.LastRemediation requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2.LastRemediationStatus vs *sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1.LastRemediationStatus) + // WARNING: in.Deprecated requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_KubeadmControlPlaneTemplate_To_v1beta2_KubeadmControlPlaneTemplate(in *KubeadmControlPlaneTemplate, out *v1beta2.KubeadmControlPlaneTemplate, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta1_KubeadmControlPlaneTemplateSpec_To_v1beta2_KubeadmControlPlaneTemplateSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_KubeadmControlPlaneTemplate_To_v1beta2_KubeadmControlPlaneTemplate is an autogenerated conversion function. +func Convert_v1beta1_KubeadmControlPlaneTemplate_To_v1beta2_KubeadmControlPlaneTemplate(in *KubeadmControlPlaneTemplate, out *v1beta2.KubeadmControlPlaneTemplate, s conversion.Scope) error { + return autoConvert_v1beta1_KubeadmControlPlaneTemplate_To_v1beta2_KubeadmControlPlaneTemplate(in, out, s) +} + +func autoConvert_v1beta2_KubeadmControlPlaneTemplate_To_v1beta1_KubeadmControlPlaneTemplate(in *v1beta2.KubeadmControlPlaneTemplate, out *KubeadmControlPlaneTemplate, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_KubeadmControlPlaneTemplateSpec_To_v1beta1_KubeadmControlPlaneTemplateSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_KubeadmControlPlaneTemplate_To_v1beta1_KubeadmControlPlaneTemplate is an autogenerated conversion function. +func Convert_v1beta2_KubeadmControlPlaneTemplate_To_v1beta1_KubeadmControlPlaneTemplate(in *v1beta2.KubeadmControlPlaneTemplate, out *KubeadmControlPlaneTemplate, s conversion.Scope) error { + return autoConvert_v1beta2_KubeadmControlPlaneTemplate_To_v1beta1_KubeadmControlPlaneTemplate(in, out, s) +} + +func autoConvert_v1beta1_KubeadmControlPlaneTemplateList_To_v1beta2_KubeadmControlPlaneTemplateList(in *KubeadmControlPlaneTemplateList, out *v1beta2.KubeadmControlPlaneTemplateList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta2.KubeadmControlPlaneTemplate, len(*in)) + for i := range *in { + if err := Convert_v1beta1_KubeadmControlPlaneTemplate_To_v1beta2_KubeadmControlPlaneTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta1_KubeadmControlPlaneTemplateList_To_v1beta2_KubeadmControlPlaneTemplateList is an autogenerated conversion function. +func Convert_v1beta1_KubeadmControlPlaneTemplateList_To_v1beta2_KubeadmControlPlaneTemplateList(in *KubeadmControlPlaneTemplateList, out *v1beta2.KubeadmControlPlaneTemplateList, s conversion.Scope) error { + return autoConvert_v1beta1_KubeadmControlPlaneTemplateList_To_v1beta2_KubeadmControlPlaneTemplateList(in, out, s) +} + +func autoConvert_v1beta2_KubeadmControlPlaneTemplateList_To_v1beta1_KubeadmControlPlaneTemplateList(in *v1beta2.KubeadmControlPlaneTemplateList, out *KubeadmControlPlaneTemplateList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KubeadmControlPlaneTemplate, len(*in)) + for i := range *in { + if err := Convert_v1beta2_KubeadmControlPlaneTemplate_To_v1beta1_KubeadmControlPlaneTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_KubeadmControlPlaneTemplateList_To_v1beta1_KubeadmControlPlaneTemplateList is an autogenerated conversion function. +func Convert_v1beta2_KubeadmControlPlaneTemplateList_To_v1beta1_KubeadmControlPlaneTemplateList(in *v1beta2.KubeadmControlPlaneTemplateList, out *KubeadmControlPlaneTemplateList, s conversion.Scope) error { + return autoConvert_v1beta2_KubeadmControlPlaneTemplateList_To_v1beta1_KubeadmControlPlaneTemplateList(in, out, s) +} + +func autoConvert_v1beta1_KubeadmControlPlaneTemplateMachineTemplate_To_v1beta2_KubeadmControlPlaneTemplateMachineTemplate(in *KubeadmControlPlaneTemplateMachineTemplate, out *v1beta2.KubeadmControlPlaneTemplateMachineTemplate, s conversion.Scope) error { + if err := Convert_v1beta1_ObjectMeta_To_v1beta2_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + // WARNING: in.NodeDrainTimeout requires manual conversion: does not exist in peer-type + // WARNING: in.NodeVolumeDetachTimeout requires manual conversion: does not exist in peer-type + // WARNING: in.NodeDeletionTimeout requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_KubeadmControlPlaneTemplateMachineTemplate_To_v1beta1_KubeadmControlPlaneTemplateMachineTemplate(in *v1beta2.KubeadmControlPlaneTemplateMachineTemplate, out *KubeadmControlPlaneTemplateMachineTemplate, s conversion.Scope) error { + if err := Convert_v1beta2_ObjectMeta_To_v1beta1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + // WARNING: in.Spec requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_KubeadmControlPlaneTemplateResource_To_v1beta2_KubeadmControlPlaneTemplateResource(in *KubeadmControlPlaneTemplateResource, out *v1beta2.KubeadmControlPlaneTemplateResource, s conversion.Scope) error { + if err := Convert_v1beta1_ObjectMeta_To_v1beta2_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1beta1_KubeadmControlPlaneTemplateResourceSpec_To_v1beta2_KubeadmControlPlaneTemplateResourceSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_KubeadmControlPlaneTemplateResource_To_v1beta2_KubeadmControlPlaneTemplateResource is an autogenerated conversion function. +func Convert_v1beta1_KubeadmControlPlaneTemplateResource_To_v1beta2_KubeadmControlPlaneTemplateResource(in *KubeadmControlPlaneTemplateResource, out *v1beta2.KubeadmControlPlaneTemplateResource, s conversion.Scope) error { + return autoConvert_v1beta1_KubeadmControlPlaneTemplateResource_To_v1beta2_KubeadmControlPlaneTemplateResource(in, out, s) +} + +func autoConvert_v1beta2_KubeadmControlPlaneTemplateResource_To_v1beta1_KubeadmControlPlaneTemplateResource(in *v1beta2.KubeadmControlPlaneTemplateResource, out *KubeadmControlPlaneTemplateResource, s conversion.Scope) error { + if err := Convert_v1beta2_ObjectMeta_To_v1beta1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1beta2_KubeadmControlPlaneTemplateResourceSpec_To_v1beta1_KubeadmControlPlaneTemplateResourceSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_KubeadmControlPlaneTemplateResource_To_v1beta1_KubeadmControlPlaneTemplateResource is an autogenerated conversion function. +func Convert_v1beta2_KubeadmControlPlaneTemplateResource_To_v1beta1_KubeadmControlPlaneTemplateResource(in *v1beta2.KubeadmControlPlaneTemplateResource, out *KubeadmControlPlaneTemplateResource, s conversion.Scope) error { + return autoConvert_v1beta2_KubeadmControlPlaneTemplateResource_To_v1beta1_KubeadmControlPlaneTemplateResource(in, out, s) +} + +func autoConvert_v1beta1_KubeadmControlPlaneTemplateResourceSpec_To_v1beta2_KubeadmControlPlaneTemplateResourceSpec(in *KubeadmControlPlaneTemplateResourceSpec, out *v1beta2.KubeadmControlPlaneTemplateResourceSpec, s conversion.Scope) error { + // WARNING: in.MachineTemplate requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1.KubeadmControlPlaneTemplateMachineTemplate vs sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2.KubeadmControlPlaneTemplateMachineTemplate) + if err := Convert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(&in.KubeadmConfigSpec, &out.KubeadmConfigSpec, s); err != nil { + return err + } + // WARNING: in.RolloutBefore requires manual conversion: does not exist in peer-type + // WARNING: in.RolloutAfter requires manual conversion: does not exist in peer-type + // WARNING: in.RolloutStrategy requires manual conversion: does not exist in peer-type + // WARNING: in.RemediationStrategy requires manual conversion: does not exist in peer-type + // WARNING: in.MachineNamingStrategy requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_KubeadmControlPlaneTemplateResourceSpec_To_v1beta1_KubeadmControlPlaneTemplateResourceSpec(in *v1beta2.KubeadmControlPlaneTemplateResourceSpec, out *KubeadmControlPlaneTemplateResourceSpec, s conversion.Scope) error { + // WARNING: in.MachineTemplate requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2.KubeadmControlPlaneTemplateMachineTemplate vs *sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1.KubeadmControlPlaneTemplateMachineTemplate) + if err := Convert_v1beta2_KubeadmConfigSpec_To_v1beta1_KubeadmConfigSpec(&in.KubeadmConfigSpec, &out.KubeadmConfigSpec, s); err != nil { + return err + } + // WARNING: in.Rollout requires manual conversion: does not exist in peer-type + // WARNING: in.Remediation requires manual conversion: does not exist in peer-type + // WARNING: in.MachineNaming requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_KubeadmControlPlaneTemplateSpec_To_v1beta2_KubeadmControlPlaneTemplateSpec(in *KubeadmControlPlaneTemplateSpec, out *v1beta2.KubeadmControlPlaneTemplateSpec, s conversion.Scope) error { + if err := Convert_v1beta1_KubeadmControlPlaneTemplateResource_To_v1beta2_KubeadmControlPlaneTemplateResource(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_KubeadmControlPlaneTemplateSpec_To_v1beta2_KubeadmControlPlaneTemplateSpec is an autogenerated conversion function. +func Convert_v1beta1_KubeadmControlPlaneTemplateSpec_To_v1beta2_KubeadmControlPlaneTemplateSpec(in *KubeadmControlPlaneTemplateSpec, out *v1beta2.KubeadmControlPlaneTemplateSpec, s conversion.Scope) error { + return autoConvert_v1beta1_KubeadmControlPlaneTemplateSpec_To_v1beta2_KubeadmControlPlaneTemplateSpec(in, out, s) +} + +func autoConvert_v1beta2_KubeadmControlPlaneTemplateSpec_To_v1beta1_KubeadmControlPlaneTemplateSpec(in *v1beta2.KubeadmControlPlaneTemplateSpec, out *KubeadmControlPlaneTemplateSpec, s conversion.Scope) error { + if err := Convert_v1beta2_KubeadmControlPlaneTemplateResource_To_v1beta1_KubeadmControlPlaneTemplateResource(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_KubeadmControlPlaneTemplateSpec_To_v1beta1_KubeadmControlPlaneTemplateSpec is an autogenerated conversion function. +func Convert_v1beta2_KubeadmControlPlaneTemplateSpec_To_v1beta1_KubeadmControlPlaneTemplateSpec(in *v1beta2.KubeadmControlPlaneTemplateSpec, out *KubeadmControlPlaneTemplateSpec, s conversion.Scope) error { + return autoConvert_v1beta2_KubeadmControlPlaneTemplateSpec_To_v1beta1_KubeadmControlPlaneTemplateSpec(in, out, s) +} + +func autoConvert_v1beta1_LastRemediationStatus_To_v1beta2_LastRemediationStatus(in *LastRemediationStatus, out *v1beta2.LastRemediationStatus, s conversion.Scope) error { + out.Machine = in.Machine + // WARNING: in.Timestamp requires manual conversion: does not exist in peer-type + if err := v1.Convert_int32_To_Pointer_int32(&in.RetryCount, &out.RetryCount, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1beta2_LastRemediationStatus_To_v1beta1_LastRemediationStatus(in *v1beta2.LastRemediationStatus, out *LastRemediationStatus, s conversion.Scope) error { + out.Machine = in.Machine + // WARNING: in.Time requires manual conversion: does not exist in peer-type + if err := v1.Convert_Pointer_int32_To_int32(&in.RetryCount, &out.RetryCount, s); err != nil { + return err + } + return nil +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/zz_generated.deepcopy.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..25c37f0417 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,533 @@ +//go:build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" + corev1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlane) DeepCopyInto(out *KubeadmControlPlane) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlane. +func (in *KubeadmControlPlane) DeepCopy() *KubeadmControlPlane { + if in == nil { + return nil + } + out := new(KubeadmControlPlane) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeadmControlPlane) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneList) DeepCopyInto(out *KubeadmControlPlaneList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KubeadmControlPlane, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneList. +func (in *KubeadmControlPlaneList) DeepCopy() *KubeadmControlPlaneList { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeadmControlPlaneList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneMachineTemplate) DeepCopyInto(out *KubeadmControlPlaneMachineTemplate) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.InfrastructureRef = in.InfrastructureRef + if in.ReadinessGates != nil { + in, out := &in.ReadinessGates, &out.ReadinessGates + *out = make([]corev1beta1.MachineReadinessGate, len(*in)) + copy(*out, *in) + } + if in.NodeDrainTimeout != nil { + in, out := &in.NodeDrainTimeout, &out.NodeDrainTimeout + *out = new(v1.Duration) + **out = **in + } + if in.NodeVolumeDetachTimeout != nil { + in, out := &in.NodeVolumeDetachTimeout, &out.NodeVolumeDetachTimeout + *out = new(v1.Duration) + **out = **in + } + if in.NodeDeletionTimeout != nil { + in, out := &in.NodeDeletionTimeout, &out.NodeDeletionTimeout + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneMachineTemplate. +func (in *KubeadmControlPlaneMachineTemplate) DeepCopy() *KubeadmControlPlaneMachineTemplate { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneMachineTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneSpec) DeepCopyInto(out *KubeadmControlPlaneSpec) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + in.MachineTemplate.DeepCopyInto(&out.MachineTemplate) + in.KubeadmConfigSpec.DeepCopyInto(&out.KubeadmConfigSpec) + if in.RolloutBefore != nil { + in, out := &in.RolloutBefore, &out.RolloutBefore + *out = new(RolloutBefore) + (*in).DeepCopyInto(*out) + } + if in.RolloutAfter != nil { + in, out := &in.RolloutAfter, &out.RolloutAfter + *out = (*in).DeepCopy() + } + if in.RolloutStrategy != nil { + in, out := &in.RolloutStrategy, &out.RolloutStrategy + *out = new(RolloutStrategy) + (*in).DeepCopyInto(*out) + } + if in.RemediationStrategy != nil { + in, out := &in.RemediationStrategy, &out.RemediationStrategy + *out = new(RemediationStrategy) + (*in).DeepCopyInto(*out) + } + if in.MachineNamingStrategy != nil { + in, out := &in.MachineNamingStrategy, &out.MachineNamingStrategy + *out = new(MachineNamingStrategy) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneSpec. +func (in *KubeadmControlPlaneSpec) DeepCopy() *KubeadmControlPlaneSpec { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneStatus) DeepCopyInto(out *KubeadmControlPlaneStatus) { + *out = *in + if in.Version != nil { + in, out := &in.Version, &out.Version + *out = new(string) + **out = **in + } + if in.FailureMessage != nil { + in, out := &in.FailureMessage, &out.FailureMessage + *out = new(string) + **out = **in + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(corev1beta1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.LastRemediation != nil { + in, out := &in.LastRemediation, &out.LastRemediation + *out = new(LastRemediationStatus) + (*in).DeepCopyInto(*out) + } + if in.V1Beta2 != nil { + in, out := &in.V1Beta2, &out.V1Beta2 + *out = new(KubeadmControlPlaneV1Beta2Status) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneStatus. +func (in *KubeadmControlPlaneStatus) DeepCopy() *KubeadmControlPlaneStatus { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneTemplate) DeepCopyInto(out *KubeadmControlPlaneTemplate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneTemplate. +func (in *KubeadmControlPlaneTemplate) DeepCopy() *KubeadmControlPlaneTemplate { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeadmControlPlaneTemplate) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneTemplateList) DeepCopyInto(out *KubeadmControlPlaneTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KubeadmControlPlaneTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneTemplateList. +func (in *KubeadmControlPlaneTemplateList) DeepCopy() *KubeadmControlPlaneTemplateList { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeadmControlPlaneTemplateList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneTemplateMachineTemplate) DeepCopyInto(out *KubeadmControlPlaneTemplateMachineTemplate) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.NodeDrainTimeout != nil { + in, out := &in.NodeDrainTimeout, &out.NodeDrainTimeout + *out = new(v1.Duration) + **out = **in + } + if in.NodeVolumeDetachTimeout != nil { + in, out := &in.NodeVolumeDetachTimeout, &out.NodeVolumeDetachTimeout + *out = new(v1.Duration) + **out = **in + } + if in.NodeDeletionTimeout != nil { + in, out := &in.NodeDeletionTimeout, &out.NodeDeletionTimeout + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneTemplateMachineTemplate. +func (in *KubeadmControlPlaneTemplateMachineTemplate) DeepCopy() *KubeadmControlPlaneTemplateMachineTemplate { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneTemplateMachineTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneTemplateResource) DeepCopyInto(out *KubeadmControlPlaneTemplateResource) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneTemplateResource. +func (in *KubeadmControlPlaneTemplateResource) DeepCopy() *KubeadmControlPlaneTemplateResource { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneTemplateResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneTemplateResourceSpec) DeepCopyInto(out *KubeadmControlPlaneTemplateResourceSpec) { + *out = *in + if in.MachineTemplate != nil { + in, out := &in.MachineTemplate, &out.MachineTemplate + *out = new(KubeadmControlPlaneTemplateMachineTemplate) + (*in).DeepCopyInto(*out) + } + in.KubeadmConfigSpec.DeepCopyInto(&out.KubeadmConfigSpec) + if in.RolloutBefore != nil { + in, out := &in.RolloutBefore, &out.RolloutBefore + *out = new(RolloutBefore) + (*in).DeepCopyInto(*out) + } + if in.RolloutAfter != nil { + in, out := &in.RolloutAfter, &out.RolloutAfter + *out = (*in).DeepCopy() + } + if in.RolloutStrategy != nil { + in, out := &in.RolloutStrategy, &out.RolloutStrategy + *out = new(RolloutStrategy) + (*in).DeepCopyInto(*out) + } + if in.RemediationStrategy != nil { + in, out := &in.RemediationStrategy, &out.RemediationStrategy + *out = new(RemediationStrategy) + (*in).DeepCopyInto(*out) + } + if in.MachineNamingStrategy != nil { + in, out := &in.MachineNamingStrategy, &out.MachineNamingStrategy + *out = new(MachineNamingStrategy) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneTemplateResourceSpec. +func (in *KubeadmControlPlaneTemplateResourceSpec) DeepCopy() *KubeadmControlPlaneTemplateResourceSpec { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneTemplateResourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneTemplateSpec) DeepCopyInto(out *KubeadmControlPlaneTemplateSpec) { + *out = *in + in.Template.DeepCopyInto(&out.Template) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneTemplateSpec. +func (in *KubeadmControlPlaneTemplateSpec) DeepCopy() *KubeadmControlPlaneTemplateSpec { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneV1Beta2Status) DeepCopyInto(out *KubeadmControlPlaneV1Beta2Status) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ReadyReplicas != nil { + in, out := &in.ReadyReplicas, &out.ReadyReplicas + *out = new(int32) + **out = **in + } + if in.AvailableReplicas != nil { + in, out := &in.AvailableReplicas, &out.AvailableReplicas + *out = new(int32) + **out = **in + } + if in.UpToDateReplicas != nil { + in, out := &in.UpToDateReplicas, &out.UpToDateReplicas + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneV1Beta2Status. +func (in *KubeadmControlPlaneV1Beta2Status) DeepCopy() *KubeadmControlPlaneV1Beta2Status { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneV1Beta2Status) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LastRemediationStatus) DeepCopyInto(out *LastRemediationStatus) { + *out = *in + in.Timestamp.DeepCopyInto(&out.Timestamp) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LastRemediationStatus. +func (in *LastRemediationStatus) DeepCopy() *LastRemediationStatus { + if in == nil { + return nil + } + out := new(LastRemediationStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineNamingStrategy) DeepCopyInto(out *MachineNamingStrategy) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineNamingStrategy. +func (in *MachineNamingStrategy) DeepCopy() *MachineNamingStrategy { + if in == nil { + return nil + } + out := new(MachineNamingStrategy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemediationStrategy) DeepCopyInto(out *RemediationStrategy) { + *out = *in + if in.MaxRetry != nil { + in, out := &in.MaxRetry, &out.MaxRetry + *out = new(int32) + **out = **in + } + out.RetryPeriod = in.RetryPeriod + if in.MinHealthyPeriod != nil { + in, out := &in.MinHealthyPeriod, &out.MinHealthyPeriod + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemediationStrategy. +func (in *RemediationStrategy) DeepCopy() *RemediationStrategy { + if in == nil { + return nil + } + out := new(RemediationStrategy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RollingUpdate) DeepCopyInto(out *RollingUpdate) { + *out = *in + if in.MaxSurge != nil { + in, out := &in.MaxSurge, &out.MaxSurge + *out = new(intstr.IntOrString) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RollingUpdate. +func (in *RollingUpdate) DeepCopy() *RollingUpdate { + if in == nil { + return nil + } + out := new(RollingUpdate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RolloutBefore) DeepCopyInto(out *RolloutBefore) { + *out = *in + if in.CertificatesExpiryDays != nil { + in, out := &in.CertificatesExpiryDays, &out.CertificatesExpiryDays + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutBefore. +func (in *RolloutBefore) DeepCopy() *RolloutBefore { + if in == nil { + return nil + } + out := new(RolloutBefore) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RolloutStrategy) DeepCopyInto(out *RolloutStrategy) { + *out = *in + if in.RollingUpdate != nil { + in, out := &in.RollingUpdate, &out.RollingUpdate + *out = new(RollingUpdate) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutStrategy. +func (in *RolloutStrategy) DeepCopy() *RolloutStrategy { + if in == nil { + return nil + } + out := new(RolloutStrategy) + in.DeepCopyInto(out) + return out +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2/kubeadm_control_plane_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2/kubeadm_control_plane_types.go index 95aeb6a5fb..e3622270b4 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2/kubeadm_control_plane_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2/kubeadm_control_plane_types.go @@ -46,10 +46,6 @@ const ( // SkipKubeProxyAnnotation annotation explicitly skips reconciling kube-proxy if set. SkipKubeProxyAnnotation = "controlplane.cluster.x-k8s.io/skip-kube-proxy" - // KubeadmClusterConfigurationAnnotation is a machine annotation that stores the json-marshalled string of KCP ClusterConfiguration. - // This annotation is used to detect any changes in ClusterConfiguration and trigger machine rollout in KCP. - KubeadmClusterConfigurationAnnotation = "controlplane.cluster.x-k8s.io/kubeadm-cluster-configuration" - // RemediationInProgressAnnotation is used to keep track that a KCP remediation is in progress, and more // specifically it tracks that the system is in between having deleted an unhealthy machine and recreating its replacement. // NOTE: if something external to CAPI removes this annotation the system cannot detect the above situation; this can lead to @@ -424,11 +420,6 @@ type KubeadmControlPlaneSpec struct { Replicas *int32 `json:"replicas,omitempty"` // version defines the desired Kubernetes version. - // Please note that if kubeadmConfigSpec.ClusterConfiguration.imageRepository is not set - // we don't allow upgrades to versions >= v1.22.0 for which kubeadm uses the old registry (k8s.gcr.io). - // Please use a newer patch version with the new registry instead. The default registries of kubeadm are: - // * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0 - // * k8s.gcr.io (old registry): all older versions // +required // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=256 diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/cluster_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/cluster_types.go index c6ff0853b1..5d6d69a9b3 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/cluster_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/cluster_types.go @@ -1092,12 +1092,12 @@ func (c *ClusterStatus) GetTypedPhase() ClusterPhase { type APIEndpoint struct { // host is the hostname on which the API server is serving. // TODO: Can't set MinLength=1 for now, because this struct is not always used in pointer fields so today we have cases where host is set to an empty string. - // +required + // +optional // +kubebuilder:validation:MaxLength=512 Host string `json:"host"` // port is the port on which the API server is serving. - // +required + // +optional Port int32 `json:"port"` } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/conversion.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/conversion.go index 03d7a7a9ad..7d289566db 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/conversion.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/conversion.go @@ -73,6 +73,17 @@ func (src *Cluster) ConvertTo(dstRaw conversion.Hub) error { return err } + if ok { + dst.Spec.Topology.ControlPlane.HealthCheck.Checks.UnhealthyMachineConditions = restored.Spec.Topology.ControlPlane.HealthCheck.Checks.UnhealthyMachineConditions + for _, restoredMD := range restored.Spec.Topology.Workers.MachineDeployments { + for i, dstMD := range dst.Spec.Topology.Workers.MachineDeployments { + if restoredMD.Name == dstMD.Name { + dst.Spec.Topology.Workers.MachineDeployments[i].HealthCheck.Checks.UnhealthyMachineConditions = restoredMD.HealthCheck.Checks.UnhealthyMachineConditions + } + } + } + } + // Recover intent for bool values converted to *bool. clusterv1.Convert_bool_To_Pointer_bool(src.Spec.Paused, ok, restored.Spec.Paused, &dst.Spec.Paused) @@ -145,6 +156,17 @@ func (src *ClusterClass) ConvertTo(dstRaw conversion.Hub) error { return err } + if ok { + dst.Spec.ControlPlane.HealthCheck.Checks.UnhealthyMachineConditions = restored.Spec.ControlPlane.HealthCheck.Checks.UnhealthyMachineConditions + for _, restoredMD := range restored.Spec.Workers.MachineDeployments { + for i, dstMD := range dst.Spec.Workers.MachineDeployments { + if restoredMD.Class == dstMD.Class { + dst.Spec.Workers.MachineDeployments[i].HealthCheck.Checks.UnhealthyMachineConditions = restoredMD.HealthCheck.Checks.UnhealthyMachineConditions + } + } + } + } + // Recover intent for bool values converted to *bool. for i, patch := range dst.Spec.Patches { for j, definition := range patch.Definitions { @@ -248,6 +270,10 @@ func (src *ClusterClass) ConvertTo(dstRaw conversion.Hub) error { dst.Status.Variables[i] = variable } + dst.Spec.KubernetesVersions = restored.Spec.KubernetesVersions + + dst.Spec.Upgrade.External.GenerateUpgradePlanExtension = restored.Spec.Upgrade.External.GenerateUpgradePlanExtension + return nil } @@ -394,6 +420,11 @@ func (src *Machine) ConvertTo(dstRaw conversion.Hub) error { // Recover other values. if ok { dst.Spec.MinReadySeconds = restored.Spec.MinReadySeconds + dst.Spec.Taints = restored.Spec.Taints + // Restore the phase, this also means that any client using v1beta1 during a round-trip + // won't be able to write the Phase field. But that's okay as the only client writing the Phase + // field should be the Machine controller. + dst.Status.Phase = restored.Status.Phase } return nil @@ -432,6 +463,17 @@ func (src *MachineSet) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.Template.Spec.MinReadySeconds = &src.Spec.MinReadySeconds } + restored := &clusterv1.MachineSet{} + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { + return err + } + + // Recover other values + if ok { + dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints + } + return nil } @@ -449,7 +491,8 @@ func (dst *MachineSet) ConvertFrom(srcRaw conversion.Hub) error { dst.Spec.MinReadySeconds = ptr.Deref(src.Spec.Template.Spec.MinReadySeconds, 0) dropEmptyStringsMachineSpec(&dst.Spec.Template.Spec) - return nil + + return utilconversion.MarshalData(src, dst) } func (src *MachineDeployment) ConvertTo(dstRaw conversion.Hub) error { @@ -474,6 +517,11 @@ func (src *MachineDeployment) ConvertTo(dstRaw conversion.Hub) error { // Recover intent for bool values converted to *bool. clusterv1.Convert_bool_To_Pointer_bool(src.Spec.Paused, ok, restored.Spec.Paused, &dst.Spec.Paused) + // Recover other values + if ok { + dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints + } + return nil } @@ -509,6 +557,8 @@ func (src *MachineHealthCheck) ConvertTo(dstRaw conversion.Hub) error { return err } + dst.Spec.Checks.UnhealthyMachineConditions = restored.Spec.Checks.UnhealthyMachineConditions + clusterv1.Convert_int32_To_Pointer_int32(src.Status.ExpectedMachines, ok, restored.Status.ExpectedMachines, &dst.Status.ExpectedMachines) clusterv1.Convert_int32_To_Pointer_int32(src.Status.CurrentHealthy, ok, restored.Status.CurrentHealthy, &dst.Status.CurrentHealthy) clusterv1.Convert_int32_To_Pointer_int32(src.Status.RemediationsAllowed, ok, restored.Status.RemediationsAllowed, &dst.Status.RemediationsAllowed) @@ -558,6 +608,11 @@ func (src *MachinePool) ConvertTo(dstRaw conversion.Hub) error { dst.Status.Initialization = initialization } + // Recover other values + if ok { + dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints + } + return nil } @@ -1637,6 +1692,13 @@ func Convert_v1beta2_MachineStatus_To_v1beta1_MachineStatus(in *clusterv1.Machin if err := autoConvert_v1beta2_MachineStatus_To_v1beta1_MachineStatus(in, out, s); err != nil { return err } + + // Convert v1beta2 Updating phase to v1beta1 Running as Updating did not exist in v1beta1. + // We don't have to support a round-trip as only the core CAPI controller should write the Phase field. + if out.Phase == "Updating" { + out.Phase = "Running" + } + if !reflect.DeepEqual(in.LastUpdated, metav1.Time{}) { out.LastUpdated = ptr.To(in.LastUpdated) } @@ -2285,8 +2347,6 @@ func convertToObjectReference(ref clusterv1.ContractVersionedObjectReference, na } func Convert_v1beta1_JSONSchemaProps_To_v1beta2_JSONSchemaProps(in *JSONSchemaProps, out *clusterv1.JSONSchemaProps, s apimachineryconversion.Scope) error { - // This conversion func is also required due to a bug in conversion gen that does not recognize the changes for converting bool to *bool. - // By implementing this func, autoConvert_v1beta1_JSONSchemaProps_To_v1beta2_JSONSchemaProps is generated properly. if err := autoConvert_v1beta1_JSONSchemaProps_To_v1beta2_JSONSchemaProps(in, out, s); err != nil { return err } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/machine_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/machine_types.go index 9665953dd4..5a37f75427 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/machine_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/machine_types.go @@ -66,10 +66,10 @@ const ( // * KCP adds its own pre-terminate hook on all Machines it controls. This is done to ensure it can later remove // the etcd member right before Machine termination (i.e. before InfraMachine deletion). // * Starting with Kubernetes v1.31 the KCP pre-terminate hook will wait for all other pre-terminate hooks to finish to - // ensure it runs last (thus ensuring that kubelet is still working while other pre-terminate hooks run). This is only done - // for v1.31 or above because the kubeadm ControlPlaneKubeletLocalMode was introduced with kubeadm 1.31. This feature configures - // the kubelet to communicate with the local apiserver. Only because of that the kubelet immediately starts failing after the etcd - // member is removed. We need the ControlPlaneKubeletLocalMode feature with 1.31 to adhere to the kubelet skew policy. + // ensure it runs last (thus ensuring that kubelet is still working while other pre-terminate hooks run). This is done + // for v1.31 or above because the kubeadm ControlPlaneKubeletLocalMode was introduced with kubeadm 1.31 (graduated to GA in 1.36). + // This feature configures the kubelet to communicate with the local apiserver. Only because of that the kubelet immediately + // starts failing after the etcd member is removed. We need the ControlPlaneKubeletLocalMode feature with 1.31+ to adhere to the kubelet skew policy. PreTerminateDeleteHookAnnotationPrefix = "pre-terminate.delete.hook.machine.cluster.x-k8s.io" // MachineCertificatesExpiryDateAnnotation annotation specifies the expiry date of the machine certificates in RFC3339 format. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.conversion.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.conversion.go index eb54f254dd..c0514e67d2 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.conversion.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.conversion.go @@ -1207,6 +1207,8 @@ func autoConvert_v1beta2_ClusterClassSpec_To_v1beta1_ClusterClassSpec(in *v1beta } else { out.Patches = nil } + // WARNING: in.Upgrade requires manual conversion: does not exist in peer-type + // WARNING: in.KubernetesVersions requires manual conversion: does not exist in peer-type return nil } @@ -3165,6 +3167,7 @@ func autoConvert_v1beta2_MachineSpec_To_v1beta1_MachineSpec(in *v1beta2.MachineS // WARNING: in.MinReadySeconds requires manual conversion: does not exist in peer-type out.ReadinessGates = *(*[]MachineReadinessGate)(unsafe.Pointer(&in.ReadinessGates)) // WARNING: in.Deletion requires manual conversion: does not exist in peer-type + // WARNING: in.Taints requires manual conversion: does not exist in peer-type return nil } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.openapi.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.openapi.go index 13a78b2371..723cf5a90c 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.openapi.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.openapi.go @@ -154,7 +154,6 @@ func schema_cluster_api_api_core_v1beta1_APIEndpoint(ref common.ReferenceCallbac }, }, }, - Required: []string{"host", "port"}, }, }, } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/cluster_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/cluster_types.go index af09698531..9666455e90 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/cluster_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/cluster_types.go @@ -80,17 +80,27 @@ const ( // failing due to an error. ClusterTopologyReconciledFailedReason = "ReconcileFailed" + // ClusterTopologyReconciledClusterCreatingReason documents reconciliation of a Cluster topology + // not yet created because the BeforeClusterCreate hook is blocking. + ClusterTopologyReconciledClusterCreatingReason = "ClusterCreating" + // ClusterTopologyReconciledControlPlaneUpgradePendingReason documents reconciliation of a Cluster topology // not yet completed because Control Plane is not yet updated to match the desired topology spec. + // + // Deprecated: please use ClusterUpgrading instead. ClusterTopologyReconciledControlPlaneUpgradePendingReason = "ControlPlaneUpgradePending" // ClusterTopologyReconciledMachineDeploymentsCreatePendingReason documents reconciliation of a Cluster topology // not yet completed because at least one of the MachineDeployments is yet to be created. // This generally happens because new MachineDeployment creations are held off while the ControlPlane is not stable. + // + // Deprecated: please use ClusterUpgrading instead. ClusterTopologyReconciledMachineDeploymentsCreatePendingReason = "MachineDeploymentsCreatePending" // ClusterTopologyReconciledMachineDeploymentsUpgradePendingReason documents reconciliation of a Cluster topology // not yet completed because at least one of the MachineDeployments is not yet updated to match the desired topology spec. + // + // Deprecated: please use ClusterUpgrading instead. ClusterTopologyReconciledMachineDeploymentsUpgradePendingReason = "MachineDeploymentsUpgradePending" // ClusterTopologyReconciledMachineDeploymentsUpgradeDeferredReason documents reconciliation of a Cluster topology @@ -99,11 +109,15 @@ const ( // ClusterTopologyReconciledMachinePoolsUpgradePendingReason documents reconciliation of a Cluster topology // not yet completed because at least one of the MachinePools is not yet updated to match the desired topology spec. + // + // Deprecated: please use ClusterUpgrading instead. ClusterTopologyReconciledMachinePoolsUpgradePendingReason = "MachinePoolsUpgradePending" // ClusterTopologyReconciledMachinePoolsCreatePendingReason documents reconciliation of a Cluster topology // not yet completed because at least one of the MachinePools is yet to be created. // This generally happens because new MachinePool creations are held off while the ControlPlane is not stable. + // + // Deprecated: please use ClusterUpgrading instead. ClusterTopologyReconciledMachinePoolsCreatePendingReason = "MachinePoolsCreatePending" // ClusterTopologyReconciledMachinePoolsUpgradeDeferredReason documents reconciliation of a Cluster topology @@ -112,8 +126,13 @@ const ( // ClusterTopologyReconciledHookBlockingReason documents reconciliation of a Cluster topology // not yet completed because at least one of the lifecycle hooks is blocking. + // + // Deprecated: please use ClusterUpgrading instead. ClusterTopologyReconciledHookBlockingReason = "LifecycleHookBlocking" + // ClusterTopologyReconciledClusterUpgradingReason documents reconciliation of a Cluster topology + // not yet completed because a cluster upgrade is still in progress. + ClusterTopologyReconciledClusterUpgradingReason = "ClusterUpgrading" // ClusterTopologyReconciledClusterClassNotReconciledReason documents reconciliation of a Cluster topology not // yet completed because the ClusterClass has not reconciled yet. If this condition persists there may be an issue // with the ClusterClass surfaced in the ClusterClass status or controller logs. @@ -725,6 +744,16 @@ type ControlPlaneTopologyHealthCheckChecks struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=100 UnhealthyNodeConditions []UnhealthyNodeCondition `json:"unhealthyNodeConditions,omitempty"` + + // unhealthyMachineConditions contains a list of the machine conditions that determine + // whether a machine is considered unhealthy. The conditions are combined in a + // logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + // + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + UnhealthyMachineConditions []UnhealthyMachineCondition `json:"unhealthyMachineConditions,omitempty"` } // ControlPlaneTopologyHealthCheckRemediation configures if and how remediations are triggered if a control plane Machine is unhealthy. @@ -975,6 +1004,16 @@ type MachineDeploymentTopologyHealthCheckChecks struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=100 UnhealthyNodeConditions []UnhealthyNodeCondition `json:"unhealthyNodeConditions,omitempty"` + + // unhealthyMachineConditions contains a list of the machine conditions that determine + // whether a machine is considered unhealthy. The conditions are combined in a + // logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + // + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + UnhealthyMachineConditions []UnhealthyMachineCondition `json:"unhealthyMachineConditions,omitempty"` } // MachineDeploymentTopologyHealthCheckRemediation configures if and how remediations are triggered if a MachineDeployment Machine is unhealthy. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/clusterclass_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/clusterclass_types.go index 80d78f3585..12e8cc19c5 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/clusterclass_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/clusterclass_types.go @@ -135,6 +135,22 @@ type ClusterClassSpec struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=1000 Patches []ClusterClassPatch `json:"patches,omitempty"` + + // upgrade defines the upgrade configuration for clusters using this ClusterClass. + // +optional + Upgrade ClusterClassUpgrade `json:"upgrade,omitempty,omitzero"` + + // kubernetesVersions is the list of Kubernetes versions that can be + // used for clusters using this ClusterClass. + // The list of version must be ordered from the older to the newer version, and there should be + // at least one version for every minor in between the first and the last version. + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=256 + KubernetesVersions []string `json:"kubernetesVersions,omitempty"` } // InfrastructureClass defines the class for the infrastructure cluster. @@ -265,6 +281,16 @@ type ControlPlaneClassHealthCheckChecks struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=100 UnhealthyNodeConditions []UnhealthyNodeCondition `json:"unhealthyNodeConditions,omitempty"` + + // unhealthyMachineConditions contains a list of the machine conditions that determine + // whether a machine is considered unhealthy. The conditions are combined in a + // logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + // + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + UnhealthyMachineConditions []UnhealthyMachineCondition `json:"unhealthyMachineConditions,omitempty"` } // ControlPlaneClassHealthCheckRemediation configures if and how remediations are triggered if a control plane Machine is unhealthy. @@ -526,6 +552,16 @@ type MachineDeploymentClassHealthCheckChecks struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=100 UnhealthyNodeConditions []UnhealthyNodeCondition `json:"unhealthyNodeConditions,omitempty"` + + // unhealthyMachineConditions contains a list of the machine conditions that determine + // whether a machine is considered unhealthy. The conditions are combined in a + // logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + // + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + UnhealthyMachineConditions []UnhealthyMachineCondition `json:"unhealthyMachineConditions,omitempty"` } // MachineDeploymentClassHealthCheckRemediation configures if and how remediations are triggered if a MachineDeployment Machine is unhealthy. @@ -1240,6 +1276,24 @@ type ClusterClassPatch struct { External *ExternalPatchDefinition `json:"external,omitempty"` } +// ClusterClassUpgrade defines the upgrade configuration for clusters using the ClusterClass. +// +kubebuilder:validation:MinProperties=1 +type ClusterClassUpgrade struct { + // external defines external runtime extensions for upgrade operations. + // +optional + External ClusterClassUpgradeExternal `json:"external,omitempty,omitzero"` +} + +// ClusterClassUpgradeExternal defines external runtime extensions for upgrade operations. +// +kubebuilder:validation:MinProperties=1 +type ClusterClassUpgradeExternal struct { + // generateUpgradePlanExtension references an extension which is called to generate upgrade plan. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + GenerateUpgradePlanExtension string `json:"generateUpgradePlanExtension,omitempty"` +} + // PatchDefinition defines a patch which is applied to customize the referenced templates. type PatchDefinition struct { // selector defines on which templates the patch should be applied. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/common_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/common_types.go index 55c37a288b..13b591c182 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/common_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/common_types.go @@ -36,6 +36,10 @@ const ( // to track the name of the MachineDeployment topology it represents. ClusterTopologyMachineDeploymentNameLabel = "topology.cluster.x-k8s.io/deployment-name" + // ClusterTopologyUpgradeStepAnnotation tracks the version of the current upgrade step. + // It is only set when an upgrade is in progress, and it contains the control plane version computed by topology controller. + ClusterTopologyUpgradeStepAnnotation = "topology.internal.cluster.x-k8s.io/upgrade-step" + // ClusterTopologyHoldUpgradeSequenceAnnotation can be used to hold the entire MachineDeployment upgrade sequence. // If the annotation is set on a MachineDeployment topology in Cluster.spec.topology.workers, the Kubernetes upgrade // for this MachineDeployment topology and all subsequent ones is deferred. @@ -95,6 +99,9 @@ const ( // AnnotationsFromMachineAnnotation is the annotation set on nodes to track the annotations that originated from machines. AnnotationsFromMachineAnnotation = "cluster.x-k8s.io/annotations-from-machine" + // TaintsFromMachineAnnotation is the annotation set on nodes to track the taints that originated from machines. + TaintsFromMachineAnnotation = "cluster.x-k8s.io/taints-from-machine" + // OwnerNameAnnotation is the annotation set on nodes identifying the owner name. OwnerNameAnnotation = "cluster.x-k8s.io/owner-name" @@ -302,7 +309,7 @@ type MachineAddress struct { } // MachineAddresses is a slice of MachineAddress items to be used by infrastructure providers. -// +kubebuilder:validation:MaxItems=32 +// +kubebuilder:validation:MaxItems=256 // +listType=atomic type MachineAddresses []MachineAddress @@ -401,3 +408,58 @@ func (r *ContractVersionedObjectReference) GroupKind() schema.GroupKind { Kind: r.Kind, } } + +// MachineTaint defines a taint equivalent to corev1.Taint, but additionally having a propagation field. +type MachineTaint struct { + // key is the taint key to be applied to a node. + // Must be a valid qualified name of maximum size 63 characters + // with an optional subdomain prefix of maximum size 253 characters, + // separated by a `/`. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=317 + // +kubebuilder:validation:Pattern=^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/)?([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$ + // +kubebuilder:validation:XValidation:rule="self.contains('/') ? ( self.split('/') [0].size() <= 253 && self.split('/') [1].size() <= 63 && self.split('/').size() == 2 ) : self.size() <= 63",message="key must be a valid qualified name of max size 63 characters with an optional subdomain prefix of max size 253 characters" + Key string `json:"key,omitempty"` + + // value is the taint value corresponding to the taint key. + // It must be a valid label value of maximum size 63 characters. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=63 + // +kubebuilder:validation:Pattern=^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ + Value string `json:"value,omitempty"` + + // effect is the effect for the taint. Valid values are NoSchedule, PreferNoSchedule and NoExecute. + // +required + // +kubebuilder:validation:Enum=NoSchedule;PreferNoSchedule;NoExecute + Effect corev1.TaintEffect `json:"effect,omitempty"` + + // propagation defines how this taint should be propagated to nodes. + // Valid values are 'Always' and 'OnInitialization'. + // Always: The taint will be continuously reconciled. If it is not set for a node, it will be added during reconciliation. + // OnInitialization: The taint will be added during node initialization. If it gets removed from the node later on it will not get added again. + // +required + Propagation MachineTaintPropagation `json:"propagation,omitempty"` +} + +// MachineTaintPropagation defines when a taint should be propagated to nodes. +// +kubebuilder:validation:Enum=Always;OnInitialization +type MachineTaintPropagation string + +const ( + // MachineTaintPropagationAlways means the taint should be continuously reconciled and kept on the node. + // - If an Always taint is added to the Machine, the taint will be added to the node. + // - If an Always taint is removed from the Machine, the taint will be removed from the node. + // - If an OnInitialization taint is changed to Always, the Machine controller will ensure the taint is set on the node. + // - If an Always taint is removed from the node, it will be re-added during reconciliation. + MachineTaintPropagationAlways MachineTaintPropagation = "Always" + + // MachineTaintPropagationOnInitialization means the taint should be set once during initialization and then + // left alone. + // - If an OnInitialization taint is added to the Machine, the taint will only be added to the node on initialization. + // - If an OnInitialization taint is removed from the Machine nothing will be changed on the node. + // - If an Always taint is changed to OnInitialization, the taint will only be added to the node on initialization. + // - If an OnInitialization taint is removed from the node, it will not be re-added during reconciliation. + MachineTaintPropagationOnInitialization MachineTaintPropagation = "OnInitialization" +) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_phase_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_phase_types.go index 1ca9551564..b849ea61d6 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_phase_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_phase_types.go @@ -45,6 +45,10 @@ const ( // become a Kubernetes Node in a Ready state. MachinePhaseRunning = MachinePhase("Running") + // MachinePhaseUpdating is the Machine state when the Machine + // is updating. + MachinePhaseUpdating = MachinePhase("Updating") + // MachinePhaseDeleting is the Machine state when a delete // request has been sent to the API Server, // but its infrastructure has not yet been fully deleted. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_types.go index a60f736ba2..16f4bf07a7 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_types.go @@ -66,10 +66,10 @@ const ( // * KCP adds its own pre-terminate hook on all Machines it controls. This is done to ensure it can later remove // the etcd member right before Machine termination (i.e. before InfraMachine deletion). // * Starting with Kubernetes v1.31 the KCP pre-terminate hook will wait for all other pre-terminate hooks to finish to - // ensure it runs last (thus ensuring that kubelet is still working while other pre-terminate hooks run). This is only done - // for v1.31 or above because the kubeadm ControlPlaneKubeletLocalMode was introduced with kubeadm 1.31. This feature configures - // the kubelet to communicate with the local apiserver. Only because of that the kubelet immediately starts failing after the etcd - // member is removed. We need the ControlPlaneKubeletLocalMode feature with 1.31 to adhere to the kubelet skew policy. + // ensure it runs last (thus ensuring that kubelet is still working while other pre-terminate hooks run). This is done + // for v1.31 or above because the kubeadm ControlPlaneKubeletLocalMode was introduced with kubeadm 1.31 (graduated to GA in 1.36). + // This feature configures the kubelet to communicate with the local apiserver. Only because of that the kubelet immediately + // starts failing after the etcd member is removed. We need the ControlPlaneKubeletLocalMode feature with 1.31+ to adhere to the kubelet skew policy. PreTerminateDeleteHookAnnotationPrefix = "pre-terminate.delete.hook.machine.cluster.x-k8s.io" // MachineCertificatesExpiryDateAnnotation annotation specifies the expiry date of the machine certificates in RFC3339 format. @@ -87,6 +87,17 @@ const ( // ManagedNodeAnnotationDomain is one of the CAPI managed Node annotation domains. ManagedNodeAnnotationDomain = "node.cluster.x-k8s.io" + + // PendingAcknowledgeMoveAnnotation is an internal annotation added by the MS controller to a machine when being + // moved from the oldMS to the newMS. The annotation is removed as soon as the MS controller get the acknowledgment about the + // replica being accounted from the corresponding MD. + // Note: The annotation is added when reconciling the oldMS, and it is removed when reconciling the newMS. + // Note: This annotation is used in pair with AcknowledgedMoveAnnotation on MachineSets. + PendingAcknowledgeMoveAnnotation = "in-place-updates.internal.cluster.x-k8s.io/pending-acknowledge-move" + + // UpdateInProgressAnnotation is an internal annotation added to machines by the controller owning the Machine when in-place update + // is started, e.g. by the MachineSet controller; the annotation will be removed by the Machine controller when in-place update is completed. + UpdateInProgressAnnotation = "in-place-updates.internal.cluster.x-k8s.io/update-in-progress" ) // Machine's Available condition and corresponding reasons. @@ -109,7 +120,7 @@ const ( // Machine's Ready condition and corresponding reasons. const ( // MachineReadyCondition is true if the Machine's deletionTimestamp is not set, Machine's BootstrapConfigReady, InfrastructureReady, - // NodeHealthy and HealthCheckSucceeded (if present) conditions are true; if other conditions are defined in spec.readinessGates, + // NodeHealthy and HealthCheckSucceeded (if present) conditions are true, Updating condition is false; if other conditions are defined in spec.readinessGates, // these conditions must be true as well. // Note: // - When summarizing the Deleting condition: @@ -151,6 +162,28 @@ const ( // MachineNotUpToDateReason surface when a Machine spec does not match the spec of the Machine's owner resource, e.g. KubeadmControlPlane or MachineDeployment. MachineNotUpToDateReason = "NotUpToDate" + + // MachineUpToDateUpdatingReason surface when a Machine spec matches the spec of the Machine's owner resource, + // but the Machine is still updating in-place. + MachineUpToDateUpdatingReason = "Updating" +) + +// Machine's Updating condition and corresponding reasons. +// Note: Updating condition is set by the Machine controller during in-place updates. +const ( + // MachineUpdatingCondition is true while an in-place update is in progress on the Machine. + // The condition is owned by the Machine controller and is used to track the progress of in-place updates. + // This condition is considered when computing the UpToDate condition. + MachineUpdatingCondition = "Updating" + + // MachineNotUpdatingReason surfaces when the Machine is not performing an in-place update. + MachineNotUpdatingReason = "NotUpdating" + + // MachineInPlaceUpdatingReason surfaces when the Machine is waiting for in-place update to complete. + MachineInPlaceUpdatingReason = "InPlaceUpdating" + + // MachineInPlaceUpdateFailedReason surfaces when the in-place update has failed. + MachineInPlaceUpdateFailedReason = "InPlaceUpdateFailed" ) // Machine's BootstrapConfigReady condition and corresponding reasons. @@ -276,6 +309,10 @@ const ( // defined by a MachineHealthCheck object. MachineHealthCheckUnhealthyNodeReason = "UnhealthyNode" + // MachineHealthCheckUnhealthyMachineReason surfaces when the machine does not pass the health checks + // defined by a MachineHealthCheck object. + MachineHealthCheckUnhealthyMachineReason = "UnhealthyMachine" + // MachineHealthCheckNodeStartupTimeoutReason surfaces when the node hosted on the machine does not appear within // the timeout defined by a MachineHealthCheck object. MachineHealthCheckNodeStartupTimeoutReason = "NodeStartupTimeout" @@ -451,6 +488,23 @@ type MachineSpec struct { // deletion contains configuration options for Machine deletion. // +optional Deletion MachineDeletionSpec `json:"deletion,omitempty,omitzero"` + + // taints are the node taints that Cluster API will manage. + // This list is not necessarily complete: other Kubernetes components may add or remove other taints from nodes, + // e.g. the node controller might add the node.kubernetes.io/not-ready taint. + // Only those taints defined in this list will be added or removed by core Cluster API controllers. + // + // There can be at most 64 taints. + // A pod would have to tolerate all existing taints to run on the corresponding node. + // + // NOTE: This list is implemented as a "map" type, meaning that individual elements can be managed by different owners. + // +optional + // +listType=map + // +listMapKey=key + // +listMapKey=effect + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=64 + Taints []MachineTaint `json:"taints,omitempty"` } // MachineDeletionSpec contains configuration options for Machine deletion. @@ -502,7 +556,7 @@ type MachineReadinessGate struct { type MachineStatus struct { // conditions represents the observations of a Machine's current state. // Known condition types are Available, Ready, UpToDate, BootstrapConfigReady, InfrastructureReady, NodeReady, - // NodeHealthy, Deleting, Paused. + // NodeHealthy, Updating, Deleting, Paused. // If a MachineHealthCheck is targeting this machine, also HealthCheckSucceeded, OwnerRemediated conditions are added. // Additionally control plane Machines controlled by KubeadmControlPlane will have following additional conditions: // APIServerPodHealthy, ControllerManagerPodHealthy, SchedulerPodHealthy, EtcdPodHealthy, EtcdMemberHealthy. @@ -537,7 +591,7 @@ type MachineStatus struct { // phase represents the current phase of machine actuation. // +optional - // +kubebuilder:validation:Enum=Pending;Provisioning;Provisioned;Running;Deleting;Deleted;Failed;Unknown + // +kubebuilder:validation:Enum=Pending;Provisioning;Provisioned;Running;Updating;Deleting;Deleted;Failed;Unknown Phase string `json:"phase,omitempty"` // certificatesExpiryDate is the expiry date of the machine certificates. @@ -695,6 +749,7 @@ func (m *MachineStatus) GetTypedPhase() MachinePhase { MachinePhaseProvisioning, MachinePhaseProvisioned, MachinePhaseRunning, + MachinePhaseUpdating, MachinePhaseDeleting, MachinePhaseDeleted, MachinePhaseFailed: diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machinehealthcheck_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machinehealthcheck_types.go index 9a7e31cae4..9a1f66bc8b 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machinehealthcheck_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machinehealthcheck_types.go @@ -111,6 +111,16 @@ type MachineHealthCheckChecks struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=100 UnhealthyNodeConditions []UnhealthyNodeCondition `json:"unhealthyNodeConditions,omitempty"` + + // unhealthyMachineConditions contains a list of the machine conditions that determine + // whether a machine is considered unhealthy. The conditions are combined in a + // logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + // + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + UnhealthyMachineConditions []UnhealthyMachineCondition `json:"unhealthyMachineConditions,omitempty"` } // MachineHealthCheckRemediation configures if and how remediations are triggered if a Machine is unhealthy. @@ -227,7 +237,33 @@ type UnhealthyNodeCondition struct { // timeoutSeconds is the duration that a node must be in a given status for, // after which the node is considered unhealthy. - // For example, with a value of "1h", the node must match the status + // For example, with a value of "3600", the node must match the status + // for at least 1 hour before being considered unhealthy. + // +required + // +kubebuilder:validation:Minimum=0 + TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty"` +} + +// UnhealthyMachineCondition represents a Machine condition type and value with a timeout +// specified as a duration. When the named condition has been in the given +// status for at least the timeout value, a machine is considered unhealthy. +type UnhealthyMachineCondition struct { + // type of Machine condition + // +kubebuilder:validation:Pattern=`^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$` + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=316 + // +kubebuilder:validation:XValidation:rule="!(self in ['Ready','Available','HealthCheckSucceeded','OwnerRemediated','ExternallyRemediated'])",message="type must not be one of: Ready, Available, HealthCheckSucceeded, OwnerRemediated, ExternallyRemediated" + // +required + Type string `json:"type,omitempty"` + + // status of the condition, one of True, False, Unknown. + // +required + // +kubebuilder:validation:Enum=True;False;Unknown + Status metav1.ConditionStatus `json:"status,omitempty"` + + // timeoutSeconds is the duration that a machine must be in a given status for, + // after which the machine is considered unhealthy. + // For example, with a value of "3600", the machine must match the status // for at least 1 hour before being considered unhealthy. // +required // +kubebuilder:validation:Minimum=0 diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machineset_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machineset_types.go index 8a5a92db7d..80cb19d362 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machineset_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machineset_types.go @@ -33,6 +33,29 @@ const ( // MachineSetFinalizer is the finalizer used by the MachineSet controller to // ensure ordered cleanup of corresponding Machines when a Machineset is being deleted. MachineSetFinalizer = "cluster.x-k8s.io/machineset" + + // MachineSetMoveMachinesToMachineSetAnnotation is an internal annotation added by the MD controller to the oldMS + // when it should scale down by moving machines that can be updated in-place to the newMS instead of deleting them. + // The annotation value is the newMS name. + // Note: This annotation is used in pair with MachineSetReceiveMachinesFromMachineSetsAnnotation to perform a two-ways check before moving a machine from oldMS to newMS: + // + // "oldMS must have: move to newMS" and "newMS must have: receive replicas from oldMS" + MachineSetMoveMachinesToMachineSetAnnotation = "in-place-updates.internal.cluster.x-k8s.io/move-machines-to-machineset" + + // MachineSetReceiveMachinesFromMachineSetsAnnotation is an internal annotation added by the MD controller to the newMS + // when it should receive replicas from oldMSs as a first step of an in-place update operation + // The annotation value is a comma separated list of oldMSs. + // Note: This annotation is used in pair with MachineSetMoveMachinesToMachineSetAnnotation to perform a two-ways check before moving a machine from oldMS to newMS: + // + // "oldMS must have: move to newMS" and "newMS must have: receive replicas from oldMS" + MachineSetReceiveMachinesFromMachineSetsAnnotation = "in-place-updates.internal.cluster.x-k8s.io/receive-machines-from-machinesets" + + // AcknowledgedMoveAnnotation is an internal annotation with a list of machines added by the MD controller + // to a MachineSet when it acknowledges a machine pending acknowledge after being moved from an oldMS. + // The annotation value is a comma separated list of Machines already acknowledged; a machine is dropped + // from this annotation as soon as pending-acknowledge-move is removed from the machine; the annotation is dropped when empty. + // Note: This annotation is used in pair with PendingAcknowledgeMoveAnnotation on Machines. + AcknowledgedMoveAnnotation = "in-place-updates.internal.cluster.x-k8s.io/acknowledged-move" ) // MachineSetSpec defines the desired state of MachineSet. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/v1beta1_condition_consts.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/v1beta1_condition_consts.go index aef565c0aa..b619c6e0d7 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/v1beta1_condition_consts.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/v1beta1_condition_consts.go @@ -157,6 +157,11 @@ const ( // UnhealthyNodeConditionV1Beta1Reason is the reason used when a machine's node has one of the MachineHealthCheck's unhealthy conditions. UnhealthyNodeConditionV1Beta1Reason = "UnhealthyNode" + + // UnhealthyMachineConditionV1Beta1Reason is the reason used when a machine has one of the MachineHealthCheck's unhealthy conditions. + // When both machine and node issues are detected, this reason takes precedence over node-related reasons + // (NodeNotFoundV1Beta1Reason, NodeStartupTimeoutV1Beta1Reason, UnhealthyNodeConditionV1Beta1Reason). + UnhealthyMachineConditionV1Beta1Reason = "UnhealthyMachine" ) const ( @@ -295,17 +300,27 @@ const ( // failing due to an error. TopologyReconcileFailedV1Beta1Reason = "TopologyReconcileFailed" + // TopologyReconciledClusterCreatingV1Beta1Reason documents reconciliation of a Cluster topology + // not yet created because the BeforeClusterCreate hook is blocking. + TopologyReconciledClusterCreatingV1Beta1Reason = "ClusterCreating" + // TopologyReconciledControlPlaneUpgradePendingV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology // not yet completed because Control Plane is not yet updated to match the desired topology spec. + // + // Deprecated: please use ClusterUpgrading instead. TopologyReconciledControlPlaneUpgradePendingV1Beta1Reason = "ControlPlaneUpgradePending" // TopologyReconciledMachineDeploymentsCreatePendingV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology // not yet completed because at least one of the MachineDeployments is yet to be created. // This generally happens because new MachineDeployment creations are held off while the ControlPlane is not stable. + // + // Deprecated: please use ClusterUpgrading instead. TopologyReconciledMachineDeploymentsCreatePendingV1Beta1Reason = "MachineDeploymentsCreatePending" // TopologyReconciledMachineDeploymentsUpgradePendingV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology // not yet completed because at least one of the MachineDeployments is not yet updated to match the desired topology spec. + // + // Deprecated: please use ClusterUpgrading instead. TopologyReconciledMachineDeploymentsUpgradePendingV1Beta1Reason = "MachineDeploymentsUpgradePending" // TopologyReconciledMachineDeploymentsUpgradeDeferredV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology @@ -314,11 +329,15 @@ const ( // TopologyReconciledMachinePoolsUpgradePendingV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology // not yet completed because at least one of the MachinePools is not yet updated to match the desired topology spec. + // + // Deprecated: please use ClusterUpgrading instead. TopologyReconciledMachinePoolsUpgradePendingV1Beta1Reason = "MachinePoolsUpgradePending" // TopologyReconciledMachinePoolsCreatePendingV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology // not yet completed because at least one of the MachinePools is yet to be created. // This generally happens because new MachinePool creations are held off while the ControlPlane is not stable. + // + // Deprecated: please use ClusterUpgrading instead. TopologyReconciledMachinePoolsCreatePendingV1Beta1Reason = "MachinePoolsCreatePending" // TopologyReconciledMachinePoolsUpgradeDeferredV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology @@ -327,8 +346,14 @@ const ( // TopologyReconciledHookBlockingV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology // not yet completed because at least one of the lifecycle hooks is blocking. + // + // Deprecated: please use ClusterUpgrading instead. TopologyReconciledHookBlockingV1Beta1Reason = "LifecycleHookBlocking" + // TopologyReconciledClusterUpgradingV1Beta1Reason documents reconciliation of a Cluster topology + // not yet completed because a cluster upgrade is still in progress. + TopologyReconciledClusterUpgradingV1Beta1Reason = "ClusterUpgrading" + // TopologyReconciledClusterClassNotReconciledV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology not // yet completed because the ClusterClass has not reconciled yet. If this condition persists there may be an issue // with the ClusterClass surfaced in the ClusterClass status or controller logs. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.deepcopy.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.deepcopy.go index 49d1f66552..5adb8a56c4 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.deepcopy.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.deepcopy.go @@ -253,6 +253,12 @@ func (in *ClusterClassSpec) DeepCopyInto(out *ClusterClassSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + out.Upgrade = in.Upgrade + if in.KubernetesVersions != nil { + in, out := &in.KubernetesVersions, &out.KubernetesVersions + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterClassSpec. @@ -363,6 +369,37 @@ func (in *ClusterClassTemplateReference) DeepCopy() *ClusterClassTemplateReferen return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterClassUpgrade) DeepCopyInto(out *ClusterClassUpgrade) { + *out = *in + out.External = in.External +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterClassUpgrade. +func (in *ClusterClassUpgrade) DeepCopy() *ClusterClassUpgrade { + if in == nil { + return nil + } + out := new(ClusterClassUpgrade) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterClassUpgradeExternal) DeepCopyInto(out *ClusterClassUpgradeExternal) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterClassUpgradeExternal. +func (in *ClusterClassUpgradeExternal) DeepCopy() *ClusterClassUpgradeExternal { + if in == nil { + return nil + } + out := new(ClusterClassUpgradeExternal) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterClassV1Beta1DeprecatedStatus) DeepCopyInto(out *ClusterClassV1Beta1DeprecatedStatus) { *out = *in @@ -803,6 +840,13 @@ func (in *ControlPlaneClassHealthCheckChecks) DeepCopyInto(out *ControlPlaneClas (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.UnhealthyMachineConditions != nil { + in, out := &in.UnhealthyMachineConditions, &out.UnhealthyMachineConditions + *out = make([]UnhealthyMachineCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneClassHealthCheckChecks. @@ -979,6 +1023,13 @@ func (in *ControlPlaneTopologyHealthCheckChecks) DeepCopyInto(out *ControlPlaneT (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.UnhealthyMachineConditions != nil { + in, out := &in.UnhealthyMachineConditions, &out.UnhealthyMachineConditions + *out = make([]UnhealthyMachineCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneTopologyHealthCheckChecks. @@ -1567,6 +1618,13 @@ func (in *MachineDeploymentClassHealthCheckChecks) DeepCopyInto(out *MachineDepl (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.UnhealthyMachineConditions != nil { + in, out := &in.UnhealthyMachineConditions, &out.UnhealthyMachineConditions + *out = make([]UnhealthyMachineCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineDeploymentClassHealthCheckChecks. @@ -2034,6 +2092,13 @@ func (in *MachineDeploymentTopologyHealthCheckChecks) DeepCopyInto(out *MachineD (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.UnhealthyMachineConditions != nil { + in, out := &in.UnhealthyMachineConditions, &out.UnhealthyMachineConditions + *out = make([]UnhealthyMachineCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineDeploymentTopologyHealthCheckChecks. @@ -2439,6 +2504,13 @@ func (in *MachineHealthCheckChecks) DeepCopyInto(out *MachineHealthCheckChecks) (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.UnhealthyMachineConditions != nil { + in, out := &in.UnhealthyMachineConditions, &out.UnhealthyMachineConditions + *out = make([]UnhealthyMachineCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineHealthCheckChecks. @@ -3369,6 +3441,11 @@ func (in *MachineSpec) DeepCopyInto(out *MachineSpec) { copy(*out, *in) } in.Deletion.DeepCopyInto(&out.Deletion) + if in.Taints != nil { + in, out := &in.Taints, &out.Taints + *out = make([]MachineTaint, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineSpec. @@ -3427,6 +3504,21 @@ func (in *MachineStatus) DeepCopy() *MachineStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineTaint) DeepCopyInto(out *MachineTaint) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineTaint. +func (in *MachineTaint) DeepCopy() *MachineTaint { + if in == nil { + return nil + } + out := new(MachineTaint) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineTemplateSpec) DeepCopyInto(out *MachineTemplateSpec) { *out = *in @@ -3664,6 +3756,26 @@ func (in *Topology) DeepCopy() *Topology { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UnhealthyMachineCondition) DeepCopyInto(out *UnhealthyMachineCondition) { + *out = *in + if in.TimeoutSeconds != nil { + in, out := &in.TimeoutSeconds, &out.TimeoutSeconds + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UnhealthyMachineCondition. +func (in *UnhealthyMachineCondition) DeepCopy() *UnhealthyMachineCondition { + if in == nil { + return nil + } + out := new(UnhealthyMachineCondition) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UnhealthyNodeCondition) DeepCopyInto(out *UnhealthyNodeCondition) { *out = *in diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.openapi.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.openapi.go index 6ada26c78c..6eecc0d326 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.openapi.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.openapi.go @@ -42,6 +42,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassStatusVariable": schema_cluster_api_api_core_v1beta2_ClusterClassStatusVariable(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassStatusVariableDefinition": schema_cluster_api_api_core_v1beta2_ClusterClassStatusVariableDefinition(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassTemplateReference": schema_cluster_api_api_core_v1beta2_ClusterClassTemplateReference(ref), + "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgrade": schema_cluster_api_api_core_v1beta2_ClusterClassUpgrade(ref), + "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgradeExternal": schema_cluster_api_api_core_v1beta2_ClusterClassUpgradeExternal(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassV1Beta1DeprecatedStatus": schema_cluster_api_api_core_v1beta2_ClusterClassV1Beta1DeprecatedStatus(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassVariable": schema_cluster_api_api_core_v1beta2_ClusterClassVariable(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassVariableMetadata": schema_cluster_api_api_core_v1beta2_ClusterClassVariableMetadata(ref), @@ -161,6 +163,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineSetV1Beta1DeprecatedStatus": schema_cluster_api_api_core_v1beta2_MachineSetV1Beta1DeprecatedStatus(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineSpec": schema_cluster_api_api_core_v1beta2_MachineSpec(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineStatus": schema_cluster_api_api_core_v1beta2_MachineStatus(ref), + "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineTaint": schema_cluster_api_api_core_v1beta2_MachineTaint(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineTemplateSpec": schema_cluster_api_api_core_v1beta2_MachineTemplateSpec(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineV1Beta1DeprecatedStatus": schema_cluster_api_api_core_v1beta2_MachineV1Beta1DeprecatedStatus(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.NetworkRanges": schema_cluster_api_api_core_v1beta2_NetworkRanges(ref), @@ -171,6 +174,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api/api/core/v1beta2.PatchSelectorMatchMachineDeploymentClass": schema_cluster_api_api_core_v1beta2_PatchSelectorMatchMachineDeploymentClass(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.PatchSelectorMatchMachinePoolClass": schema_cluster_api_api_core_v1beta2_PatchSelectorMatchMachinePoolClass(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.Topology": schema_cluster_api_api_core_v1beta2_Topology(ref), + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition": schema_cluster_api_api_core_v1beta2_UnhealthyMachineCondition(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition": schema_cluster_api_api_core_v1beta2_UnhealthyNodeCondition(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ValidationRule": schema_cluster_api_api_core_v1beta2_ValidationRule(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.VariableSchema": schema_cluster_api_api_core_v1beta2_VariableSchema(ref), @@ -617,12 +621,39 @@ func schema_cluster_api_api_core_v1beta2_ClusterClassSpec(ref common.ReferenceCa }, }, }, + "upgrade": { + SchemaProps: spec.SchemaProps{ + Description: "upgrade defines the upgrade configuration for clusters using this ClusterClass.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgrade"), + }, + }, + "kubernetesVersions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "kubernetesVersions is the list of Kubernetes versions that can be used for clusters using this ClusterClass. The list of version must be ordered from the older to the newer version, and there should be at least one version for every minor in between the first and the last version.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, Required: []string{"infrastructure", "controlPlane"}, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterAvailabilityGate", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassPatch", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassVariable", "sigs.k8s.io/cluster-api/api/core/v1beta2.ControlPlaneClass", "sigs.k8s.io/cluster-api/api/core/v1beta2.InfrastructureClass", "sigs.k8s.io/cluster-api/api/core/v1beta2.WorkersClass"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterAvailabilityGate", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassPatch", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgrade", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassVariable", "sigs.k8s.io/cluster-api/api/core/v1beta2.ControlPlaneClass", "sigs.k8s.io/cluster-api/api/core/v1beta2.InfrastructureClass", "sigs.k8s.io/cluster-api/api/core/v1beta2.WorkersClass"}, } } @@ -823,6 +854,48 @@ func schema_cluster_api_api_core_v1beta2_ClusterClassTemplateReference(ref commo } } +func schema_cluster_api_api_core_v1beta2_ClusterClassUpgrade(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterClassUpgrade defines the upgrade configuration for clusters using the ClusterClass.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "external": { + SchemaProps: spec.SchemaProps{ + Description: "external defines external runtime extensions for upgrade operations.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgradeExternal"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgradeExternal"}, + } +} + +func schema_cluster_api_api_core_v1beta2_ClusterClassUpgradeExternal(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterClassUpgradeExternal defines external runtime extensions for upgrade operations.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "generateUpgradePlanExtension": { + SchemaProps: spec.SchemaProps{ + Description: "generateUpgradePlanExtension references an extension which is called to generate upgrade plan.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_cluster_api_api_core_v1beta2_ClusterClassV1Beta1DeprecatedStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1616,11 +1689,30 @@ func schema_cluster_api_api_core_v1beta2_ControlPlaneClassHealthCheckChecks(ref }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -1899,11 +1991,30 @@ func schema_cluster_api_api_core_v1beta2_ControlPlaneTopologyHealthCheckChecks(r }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -2920,11 +3031,30 @@ func schema_cluster_api_api_core_v1beta2_MachineDeploymentClassHealthCheckChecks }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -3700,11 +3830,30 @@ func schema_cluster_api_api_core_v1beta2_MachineDeploymentTopologyHealthCheckChe }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -4332,11 +4481,30 @@ func schema_cluster_api_api_core_v1beta2_MachineHealthCheckChecks(ref common.Ref }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -5996,12 +6164,35 @@ func schema_cluster_api_api_core_v1beta2_MachineSpec(ref common.ReferenceCallbac Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.MachineDeletionSpec"), }, }, + "taints": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "key", + "effect", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "taints are the node taints that Cluster API will manage. This list is not necessarily complete: other Kubernetes components may add or remove other taints from nodes, e.g. the node controller might add the node.kubernetes.io/not-ready taint. Only those taints defined in this list will be added or removed by core Cluster API controllers.\n\nThere can be at most 64 taints. A pod would have to tolerate all existing taints to run on the corresponding node.\n\nNOTE: This list is implemented as a \"map\" type, meaning that individual elements can be managed by different owners.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.MachineTaint"), + }, + }, + }, + }, + }, }, Required: []string{"clusterName", "bootstrap", "infrastructureRef"}, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.Bootstrap", "sigs.k8s.io/cluster-api/api/core/v1beta2.ContractVersionedObjectReference", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineDeletionSpec", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineReadinessGate"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.Bootstrap", "sigs.k8s.io/cluster-api/api/core/v1beta2.ContractVersionedObjectReference", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineDeletionSpec", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineReadinessGate", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineTaint"}, } } @@ -6022,7 +6213,7 @@ func schema_cluster_api_api_core_v1beta2_MachineStatus(ref common.ReferenceCallb }, }, SchemaProps: spec.SchemaProps{ - Description: "conditions represents the observations of a Machine's current state. Known condition types are Available, Ready, UpToDate, BootstrapConfigReady, InfrastructureReady, NodeReady, NodeHealthy, Deleting, Paused. If a MachineHealthCheck is targeting this machine, also HealthCheckSucceeded, OwnerRemediated conditions are added. Additionally control plane Machines controlled by KubeadmControlPlane will have following additional conditions: APIServerPodHealthy, ControllerManagerPodHealthy, SchedulerPodHealthy, EtcdPodHealthy, EtcdMemberHealthy.", + Description: "conditions represents the observations of a Machine's current state. Known condition types are Available, Ready, UpToDate, BootstrapConfigReady, InfrastructureReady, NodeReady, NodeHealthy, Updating, Deleting, Paused. If a MachineHealthCheck is targeting this machine, also HealthCheckSucceeded, OwnerRemediated conditions are added. Additionally control plane Machines controlled by KubeadmControlPlane will have following additional conditions: APIServerPodHealthy, ControllerManagerPodHealthy, SchedulerPodHealthy, EtcdPodHealthy, EtcdMemberHealthy.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -6114,6 +6305,48 @@ func schema_cluster_api_api_core_v1beta2_MachineStatus(ref common.ReferenceCallb } } +func schema_cluster_api_api_core_v1beta2_MachineTaint(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "MachineTaint defines a taint equivalent to corev1.Taint, but additionally having a propagation field.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "key is the taint key to be applied to a node. Must be a valid qualified name of maximum size 63 characters with an optional subdomain prefix of maximum size 253 characters, separated by a `/`.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "value is the taint value corresponding to the taint key. It must be a valid label value of maximum size 63 characters.", + Type: []string{"string"}, + Format: "", + }, + }, + "effect": { + SchemaProps: spec.SchemaProps{ + Description: "effect is the effect for the taint. Valid values are NoSchedule, PreferNoSchedule and NoExecute.", + Type: []string{"string"}, + Format: "", + }, + }, + "propagation": { + SchemaProps: spec.SchemaProps{ + Description: "propagation defines how this taint should be propagated to nodes. Valid values are 'Always' and 'OnInitialization'. Always: The taint will be continuously reconciled. If it is not set for a node, it will be added during reconciliation. OnInitialization: The taint will be added during node initialization. If it gets removed from the node later on it will not get added again.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"key", "effect", "propagation"}, + }, + }, + } +} + func schema_cluster_api_api_core_v1beta2_MachineTemplateSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -6518,6 +6751,41 @@ func schema_cluster_api_api_core_v1beta2_Topology(ref common.ReferenceCallback) } } +func schema_cluster_api_api_core_v1beta2_UnhealthyMachineCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UnhealthyMachineCondition represents a Machine condition type and value with a timeout specified as a duration. When the named condition has been in the given status for at least the timeout value, a machine is considered unhealthy.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "type of Machine condition", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "timeoutSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "timeoutSeconds is the duration that a machine must be in a given status for, after which the machine is considered unhealthy. For example, with a value of \"3600\", the machine must match the status for at least 1 hour before being considered unhealthy.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"type", "status", "timeoutSeconds"}, + }, + }, + } +} + func schema_cluster_api_api_core_v1beta2_UnhealthyNodeCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -6541,7 +6809,7 @@ func schema_cluster_api_api_core_v1beta2_UnhealthyNodeCondition(ref common.Refer }, "timeoutSeconds": { SchemaProps: spec.SchemaProps{ - Description: "timeoutSeconds is the duration that a node must be in a given status for, after which the node is considered unhealthy. For example, with a value of \"1h\", the node must match the status for at least 1 hour before being considered unhealthy.", + Description: "timeoutSeconds is the duration that a node must be in a given status for, after which the node is considered unhealthy. For example, with a value of \"3600\", the node must match the status for at least 1 hour before being considered unhealthy.", Type: []string{"integer"}, Format: "int32", }, diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/common_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/common_types.go index 8838dfaa6a..f184c9c7f9 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/common_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/common_types.go @@ -134,3 +134,15 @@ func (r *CommonRetryResponse) GetRetryAfterSeconds() int32 { func (r *CommonRetryResponse) SetRetryAfterSeconds(retryAfterSeconds int32) { r.RetryAfterSeconds = retryAfterSeconds } + +// PatchType defines the supported patch types. +// +kubebuilder:validation:Enum=JSONPatch;JSONMergePatch +type PatchType string + +const ( + // JSONPatchType identifies a https://datatracker.ietf.org/doc/html/rfc6902 JSON patch. + JSONPatchType PatchType = "JSONPatch" + + // JSONMergePatchType identifies a https://datatracker.ietf.org/doc/html/rfc7386 JSON merge patch. + JSONMergePatchType PatchType = "JSONMergePatch" +) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/inplaceupdate_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/inplaceupdate_types.go new file mode 100644 index 0000000000..a301c39e9a --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/inplaceupdate_types.go @@ -0,0 +1,254 @@ +/* +Copyright 2025 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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" +) + +// CanUpdateMachineRequest is the request of the CanUpdateMachine hook. +// +kubebuilder:object:root=true +type CanUpdateMachineRequest struct { + metav1.TypeMeta `json:",inline"` + + // CommonRequest contains fields common to all request types. + CommonRequest `json:",inline"` + + // current contains the current state of the Machine and related objects. + // +required + Current CanUpdateMachineRequestObjects `json:"current,omitempty,omitzero"` + + // desired contains the desired state of the Machine and related objects. + // +required + Desired CanUpdateMachineRequestObjects `json:"desired,omitempty,omitzero"` +} + +// CanUpdateMachineRequestObjects groups objects for CanUpdateMachineRequest. +type CanUpdateMachineRequestObjects struct { + // machine is the full Machine object. + // +required + Machine clusterv1.Machine `json:"machine,omitempty,omitzero"` + + // infrastructureMachine is the infra Machine object. + // +required + InfrastructureMachine runtime.RawExtension `json:"infrastructureMachine,omitempty,omitzero"` + + // bootstrapConfig is the bootstrap config object. + // +optional + BootstrapConfig runtime.RawExtension `json:"bootstrapConfig,omitempty,omitzero"` +} + +var _ ResponseObject = &CanUpdateMachineResponse{} + +// CanUpdateMachineResponse is the response of the CanUpdateMachine hook. +// +kubebuilder:object:root=true +type CanUpdateMachineResponse struct { + metav1.TypeMeta `json:",inline"` + + // CommonResponse contains Status and Message fields common to all response types. + CommonResponse `json:",inline"` + + // machinePatch when applied to the current Machine spec, indicates changes handled in-place. + // Only fields in spec have to be covered by the patch. + // +optional + MachinePatch Patch `json:"machinePatch,omitempty,omitzero"` + + // infrastructureMachinePatch indicates infra Machine spec changes handled in-place. + // Only fields in spec have to be covered by the patch. + // +optional + InfrastructureMachinePatch Patch `json:"infrastructureMachinePatch,omitempty,omitzero"` + + // bootstrapConfigPatch indicates bootstrap config spec changes handled in-place. + // Only fields in spec have to be covered by the patch. + // +optional + BootstrapConfigPatch Patch `json:"bootstrapConfigPatch,omitempty,omitzero"` +} + +// Patch is a single patch (JSONPatch or JSONMergePatch) which can include multiple operations. +type Patch struct { + // patchType JSONPatch or JSONMergePatch. + // +required + PatchType PatchType `json:"patchType,omitempty"` + + // patch data for the target object. + // +required + Patch []byte `json:"patch,omitempty"` +} + +// IsDefined returns true if one of the fields of Patch is set. +func (p *Patch) IsDefined() bool { + return p.PatchType != "" || len(p.Patch) > 0 +} + +// CanUpdateMachine is the hook that will be called to determine if an extension +// can handle specific machine changes for in-place updates. +func CanUpdateMachine(*CanUpdateMachineRequest, *CanUpdateMachineResponse) {} + +// CanUpdateMachineSetRequest is the request of the CanUpdateMachineSet hook. +// +kubebuilder:object:root=true +type CanUpdateMachineSetRequest struct { + metav1.TypeMeta `json:",inline"` + + // CommonRequest contains fields common to all request types. + CommonRequest `json:",inline"` + + // current contains the current state of the MachineSet and related objects. + // +required + Current CanUpdateMachineSetRequestObjects `json:"current,omitempty,omitzero"` + + // desired contains the desired state of the MachineSet and related objects. + // +required + Desired CanUpdateMachineSetRequestObjects `json:"desired,omitempty,omitzero"` +} + +// CanUpdateMachineSetRequestObjects groups objects for CanUpdateMachineSetRequest. +type CanUpdateMachineSetRequestObjects struct { + // machineSet is the full MachineSet object. + // Only fields in spec.template.spec have to be covered by the patch. + // +required + MachineSet clusterv1.MachineSet `json:"machineSet,omitempty,omitzero"` + + // infrastructureMachineTemplate is the provider-specific InfrastructureMachineTemplate object. + // Only fields in spec.template.spec have to be covered by the patch. + // +required + InfrastructureMachineTemplate runtime.RawExtension `json:"infrastructureMachineTemplate,omitempty,omitzero"` + + // bootstrapConfigTemplate is the provider-specific BootstrapConfigTemplate object. + // Only fields in spec.template.spec have to be covered by the patch. + // +optional + BootstrapConfigTemplate runtime.RawExtension `json:"bootstrapConfigTemplate,omitempty,omitzero"` +} + +var _ ResponseObject = &CanUpdateMachineSetResponse{} + +// CanUpdateMachineSetResponse is the response of the CanUpdateMachineSet hook. +// +kubebuilder:object:root=true +type CanUpdateMachineSetResponse struct { + metav1.TypeMeta `json:",inline"` + + // CommonResponse contains Status and Message fields common to all response types. + CommonResponse `json:",inline"` + + // machineSetPatch when applied to the current MachineSet spec, indicates changes handled in-place. + // +optional + MachineSetPatch Patch `json:"machineSetPatch,omitempty,omitzero"` + + // infrastructureMachineTemplatePatch indicates infra template spec changes handled in-place. + // +optional + InfrastructureMachineTemplatePatch Patch `json:"infrastructureMachineTemplatePatch,omitempty,omitzero"` + + // bootstrapConfigTemplatePatch indicates bootstrap template spec changes handled in-place. + // +optional + BootstrapConfigTemplatePatch Patch `json:"bootstrapConfigTemplatePatch,omitempty,omitzero"` +} + +// CanUpdateMachineSet is the hook that will be called to determine if an extension +// can handle specific MachineSet changes for in-place updates. +func CanUpdateMachineSet(*CanUpdateMachineSetRequest, *CanUpdateMachineSetResponse) {} + +// UpdateMachineRequest is the request of the UpdateMachine hook. +// +kubebuilder:object:root=true +type UpdateMachineRequest struct { + metav1.TypeMeta `json:",inline"` + + // CommonRequest contains fields common to all request types. + CommonRequest `json:",inline"` + + // desired contains the desired state of the Machine and related objects. + // +required + Desired UpdateMachineRequestObjects `json:"desired,omitempty,omitzero"` +} + +// UpdateMachineRequestObjects groups objects for UpdateMachineRequest. +type UpdateMachineRequestObjects struct { + // machine is the full Machine object. + // +required + Machine clusterv1.Machine `json:"machine,omitempty,omitzero"` + + // infrastructureMachine is the infra Machine object. + // +required + InfrastructureMachine runtime.RawExtension `json:"infrastructureMachine,omitempty,omitzero"` + + // bootstrapConfig is the bootstrap config object. + // +optional + BootstrapConfig runtime.RawExtension `json:"bootstrapConfig,omitempty,omitzero"` +} + +var _ RetryResponseObject = &UpdateMachineResponse{} + +// UpdateMachineResponse is the response of the UpdateMachine hook. +// The status of the update operation is determined by the CommonRetryResponse fields: +// - Status=Success + RetryAfterSeconds > 0: update is in progress +// - Status=Success + RetryAfterSeconds = 0: update completed successfully +// - Status=Failure: update failed +// +kubebuilder:object:root=true +type UpdateMachineResponse struct { + metav1.TypeMeta `json:",inline"` + + // CommonRetryResponse contains Status, Message and RetryAfterSeconds fields. + CommonRetryResponse `json:",inline"` +} + +// UpdateMachine is the hook that will be called to perform in-place updates on a machine. +// This hook should be idempotent and can be called multiple times for the same machine +// until it reports Done or Failed status. +func UpdateMachine(*UpdateMachineRequest, *UpdateMachineResponse) {} + +func init() { + catalogBuilder.RegisterHook(CanUpdateMachine, &runtimecatalog.HookMeta{ + Tags: []string{"In-Place Update Hooks"}, + Summary: "Cluster API Runtime will call this hook to determine if an extension can handle specific Machine changes", + Description: "Called during update planning to determine if an extension can handle Machine changes. " + + "The request contains current and desired state for Machine, InfraMachine and optionally BootstrapConfig. " + + "Extensions should return per-object patches to be applied on current objects to indicate which changes they can handle in-place.\n" + + "\n" + + "Notes:\n" + + "- This hook is called during the planning phase of updates\n" + + "- Only spec is provided, status fields are not included\n" + + "- If no extension can cover the required changes, CAPI will fallback to rolling updates\n" + + "- Only fields in Machine/InfraMachine/BootstrapConfig spec have to be covered by patches\n", + }) + + catalogBuilder.RegisterHook(CanUpdateMachineSet, &runtimecatalog.HookMeta{ + Tags: []string{"In-Place Update Hooks"}, + Summary: "Cluster API Runtime will call this hook to determine if an extension can handle specific MachineSet changes", + Description: "Called during update planning to determine if an extension can handle MachineSet changes. " + + "The request contains current and desired state for MachineSet, InfraMachineTemplate and optionally BootstrapConfigTemplate. " + + "Extensions should return per-object patches to be applied on current objects to indicate which changes they can handle in-place.\n" + + "\n" + + "Notes:\n" + + "- This hook is called during the planning phase of updates\n" + + "- Only spec is provided, status fields are not included\n" + + "- If no extension can cover the required changes, CAPI will fallback to rolling updates\n" + + "- Only fields in MachineSet/InfraMachineTemplate/BootstrapConfigTemplate spec.template.spec have to be covered by patches\n", + }) + + catalogBuilder.RegisterHook(UpdateMachine, &runtimecatalog.HookMeta{ + Tags: []string{"In-Place Update Hooks"}, + Summary: "Cluster API Runtime will call this hook to perform in-place updates on a Machine", + Description: "Cluster API Runtime will call this hook to perform the actual in-place update on a Machine. " + + "The request contains the desired state for Machine, InfraMachine and optionally BootstrapConfig. " + + "The hook will be called repeatedly until it reports Done or Failed status.\n" + + "\n" + + "Notes:\n" + + "- This hook must be idempotent - it can be called multiple times for the same Machine\n", + }) +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/lifecyclehooks_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/lifecyclehooks_types.go index bf0b8e1538..71c6455a0f 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/lifecyclehooks_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/lifecyclehooks_types.go @@ -97,6 +97,22 @@ type BeforeClusterUpgradeRequest struct { // toKubernetesVersion is the target Kubernetes version of the upgrade. // +required ToKubernetesVersion string `json:"toKubernetesVersion"` + + // controlPlaneUpgrades is the list of version upgrade steps for the control plane. + // +optional + ControlPlaneUpgrades []UpgradeStepInfo `json:"controlPlaneUpgrades,omitempty"` + + // workersUpgrades is the list of version upgrade steps for the workers. + // +optional + WorkersUpgrades []UpgradeStepInfo `json:"workersUpgrades,omitempty"` +} + +// UpgradeStepInfo provide info about a single version upgrade step. +type UpgradeStepInfo struct { + // version is the Kubernetes version for this upgrade step. + // +required + // +kubebuilder:validation:MinLength=1 + Version string `json:"version,omitempty"` } var _ RetryResponseObject = &BeforeClusterUpgradeResponse{} @@ -114,6 +130,50 @@ type BeforeClusterUpgradeResponse struct { // before the updated version is propagated to the underlying objects. func BeforeClusterUpgrade(*BeforeClusterUpgradeRequest, *BeforeClusterUpgradeResponse) {} +// BeforeControlPlaneUpgradeRequest is the request of the BeforeControlPlane hook. +// +kubebuilder:object:root=true +type BeforeControlPlaneUpgradeRequest struct { + metav1.TypeMeta `json:",inline"` + + // CommonRequest contains fields common to all request types. + CommonRequest `json:",inline"` + + // cluster is the cluster object the lifecycle hook corresponds to. + // +required + Cluster clusterv1beta1.Cluster `json:"cluster"` + + // fromKubernetesVersion is the current Kubernetes version of the control plane for the next upgrade step. + // +required + FromKubernetesVersion string `json:"fromKubernetesVersion"` + + // toKubernetesVersion is the target Kubernetes version of the control plane for the next upgrade step. + // +required + ToKubernetesVersion string `json:"toKubernetesVersion"` + + // controlPlaneUpgrades is the list of the remaining version upgrade steps for the control plane, if any. + // +optional + ControlPlaneUpgrades []UpgradeStepInfo `json:"controlPlaneUpgrades,omitempty"` + + // workersUpgrades is the list of the remaining version upgrade steps for workers, if any. + // +optional + WorkersUpgrades []UpgradeStepInfo `json:"workersUpgrades,omitempty"` +} + +var _ RetryResponseObject = &BeforeControlPlaneUpgradeResponse{} + +// BeforeControlPlaneUpgradeResponse is the response of the BeforeControlPlaneUpgrade hook. +// +kubebuilder:object:root=true +type BeforeControlPlaneUpgradeResponse struct { + metav1.TypeMeta `json:",inline"` + + // CommonRetryResponse contains Status, Message and RetryAfterSeconds fields. + CommonRetryResponse `json:",inline"` +} + +// BeforeControlPlaneUpgrade is the hook that will be called before a new version is propagated to the control plane object. +func BeforeControlPlaneUpgrade(*BeforeControlPlaneUpgradeRequest, *BeforeControlPlaneUpgradeResponse) { +} + // AfterControlPlaneUpgradeRequest is the request of the AfterControlPlaneUpgrade hook. // +kubebuilder:object:root=true type AfterControlPlaneUpgradeRequest struct { @@ -126,9 +186,17 @@ type AfterControlPlaneUpgradeRequest struct { // +required Cluster clusterv1beta1.Cluster `json:"cluster"` - // kubernetesVersion is the Kubernetes version of the Control Plane after the upgrade. + // kubernetesVersion is the Kubernetes version of the control plane after an upgrade step. // +required KubernetesVersion string `json:"kubernetesVersion"` + + // controlPlaneUpgrades is the list of the remaining version upgrade steps for the control plane, if any. + // +optional + ControlPlaneUpgrades []UpgradeStepInfo `json:"controlPlaneUpgrades,omitempty"` + + // workersUpgrades is the list of the remaining version upgrade steps for workers, if any. + // +optional + WorkersUpgrades []UpgradeStepInfo `json:"workersUpgrades,omitempty"` } var _ RetryResponseObject = &AfterControlPlaneUpgradeResponse{} @@ -146,6 +214,90 @@ type AfterControlPlaneUpgradeResponse struct { // Kubernetes version and before the target version is propagated to the workload machines. func AfterControlPlaneUpgrade(*AfterControlPlaneUpgradeRequest, *AfterControlPlaneUpgradeResponse) {} +// BeforeWorkersUpgradeRequest is the request of the BeforeWorkersUpgrade hook. +// +kubebuilder:object:root=true +type BeforeWorkersUpgradeRequest struct { + metav1.TypeMeta `json:",inline"` + + // CommonRequest contains fields common to all request types. + CommonRequest `json:",inline"` + + // cluster is the cluster object the lifecycle hook corresponds to. + // +required + Cluster clusterv1beta1.Cluster `json:"cluster"` + + // fromKubernetesVersion is the current Kubernetes version of the workers for the next upgrade step. + // +required + FromKubernetesVersion string `json:"fromKubernetesVersion"` + + // toKubernetesVersion is the target Kubernetes version of the workers for the next upgrade step. + // +required + ToKubernetesVersion string `json:"toKubernetesVersion"` + + // controlPlaneUpgrades is the list of the remaining version upgrade steps for the control plane, if any. + // +optional + ControlPlaneUpgrades []UpgradeStepInfo `json:"controlPlaneUpgrades,omitempty"` + + // workersUpgrades is the list of the remaining version upgrade steps for workers, if any. + // +optional + WorkersUpgrades []UpgradeStepInfo `json:"workersUpgrades,omitempty"` +} + +var _ RetryResponseObject = &BeforeWorkersUpgradeResponse{} + +// BeforeWorkersUpgradeResponse is the response of the BeforeWorkersUpgrade hook. +// +kubebuilder:object:root=true +type BeforeWorkersUpgradeResponse struct { + metav1.TypeMeta `json:",inline"` + + // CommonRetryResponse contains Status, Message and RetryAfterSeconds fields. + CommonRetryResponse `json:",inline"` +} + +// BeforeWorkersUpgrade is the hook that will be called before a new version is propagated to workers. +func BeforeWorkersUpgrade(*BeforeWorkersUpgradeRequest, *BeforeWorkersUpgradeResponse) { +} + +// AfterWorkersUpgradeRequest is the request of the AfterWorkersUpgrade hook. +// +kubebuilder:object:root=true +type AfterWorkersUpgradeRequest struct { + metav1.TypeMeta `json:",inline"` + + // CommonRequest contains fields common to all request types. + CommonRequest `json:",inline"` + + // cluster is the cluster object the lifecycle hook corresponds to. + // +required + Cluster clusterv1beta1.Cluster `json:"cluster"` + + // kubernetesVersion is the Kubernetes version of the workers after an upgrade step. + // +required + KubernetesVersion string `json:"kubernetesVersion"` + + // controlPlaneUpgrades is the list of the remaining version upgrade steps for the control plane, if any. + // +optional + ControlPlaneUpgrades []UpgradeStepInfo `json:"controlPlaneUpgrades,omitempty"` + + // workersUpgrades is the list of the remaining version upgrade steps for workers, if any. + // +optional + WorkersUpgrades []UpgradeStepInfo `json:"workersUpgrades,omitempty"` +} + +var _ RetryResponseObject = &AfterWorkersUpgradeResponse{} + +// AfterWorkersUpgradeResponse is the response of the AfterWorkersUpgrade hook. +// +kubebuilder:object:root=true +type AfterWorkersUpgradeResponse struct { + metav1.TypeMeta `json:",inline"` + + // CommonRetryResponse contains Status, Message and RetryAfterSeconds fields. + CommonRetryResponse `json:",inline"` +} + +// AfterWorkersUpgrade is the hook called after the control plane is successfully upgraded to the target +// Kubernetes version and before the target version is propagated to the workload machines. +func AfterWorkersUpgrade(*AfterWorkersUpgradeRequest, *AfterWorkersUpgradeResponse) {} + // AfterClusterUpgradeRequest is the request of the AfterClusterUpgrade hook. // +kubebuilder:object:root=true type AfterClusterUpgradeRequest struct { @@ -170,8 +322,8 @@ var _ ResponseObject = &AfterClusterUpgradeResponse{} type AfterClusterUpgradeResponse struct { metav1.TypeMeta `json:",inline"` - // CommonResponse contains Status and Message fields common to all response types. - CommonResponse `json:",inline"` + // CommonRetryResponse contains Status, Message and RetryAfterSeconds fields. + CommonRetryResponse `json:",inline"` } // AfterClusterUpgrade is the hook that is called after the entire cluster is updated @@ -243,18 +395,58 @@ func init() { "tasks before the new version is propagated to the control plane", }) + catalogBuilder.RegisterHook(BeforeControlPlaneUpgrade, &runtimecatalog.HookMeta{ + Tags: []string{"Lifecycle Hooks"}, + Summary: "Cluster API Runtime will call this hook before the control plane is upgraded", + Description: "This hook is called before a new version is propagated to the control plane object.\n" + + "\n" + + "Notes:\n" + + "- This hook will be called only for Clusters with a managed topology\n" + + "- When an upgrade is starting, BeforeControlPlaneUpgrade will be called after BeforeClusterUpgrade is completed\n" + + "- When an upgrade is in progress BeforeControlPlaneUpgrade will be called for each intermediate version that will be applied " + + "to the control plane (instead BeforeClusterUpgrade will be called only once at the beginning of the upgrade)" + + "- This is a blocking hook; Runtime Extension implementers can use this hook to execute " + + "tasks before the new version is propagated to the control plane", + }) + catalogBuilder.RegisterHook(AfterControlPlaneUpgrade, &runtimecatalog.HookMeta{ Tags: []string{"Lifecycle Hooks"}, Summary: "Cluster API Runtime will call this hook after the control plane is upgraded", Description: "Cluster API Runtime will call this hook after the a cluster's control plane has been upgraded to the version specified " + - "in spec.topology.version, and immediately before the new version is going to be propagated to the MachineDeployments. " + + "in spec.topology.version or to an intermediate version in the upgrade plan." + "A control plane upgrade is completed when all the machines in the control plane have been upgraded.\n" + "\n" + "Notes:\n" + "- This hook will be called only for Clusters with a managed topology\n" + "- The call's request contains the Cluster object and the Kubernetes version we upgraded to\n" + "- This is a blocking hook; Runtime Extension implementers can use this hook to execute " + - "tasks before the new version is propagated to the MachineDeployments", + "tasks before the new version is propagated to the MachineDeployments and Machine Pools", + }) + + catalogBuilder.RegisterHook(BeforeWorkersUpgrade, &runtimecatalog.HookMeta{ + Tags: []string{"Lifecycle Hooks"}, + Summary: "Cluster API Runtime will call this hook before the workers are upgraded", + Description: "This hook is called before a new version is propagated to workers.\n" + + "\n" + + "Notes:\n" + + "- This hook will be called only for Clusters with a managed topology\n" + + "- This hook will be called only if workers upgrade must be performed for an intermediate version of " + + "a chained upgrade or when upgrading to the target spec.topology.version.\n" + + "- This is a blocking hook; Runtime Extension implementers can use this hook to execute " + + "tasks before the new version is propagated to the MachineDeployments and Machine Pools", + }) + + catalogBuilder.RegisterHook(AfterWorkersUpgrade, &runtimecatalog.HookMeta{ + Tags: []string{"Lifecycle Hooks"}, + Summary: "Cluster API Runtime will call this hook after workers are upgraded", + Description: "This hook is called after all the workers have been upgraded to the version specified in spec.topology.version " + + "or to an intermediate version in the upgrade plan.\n" + + "\n" + + "Notes:\n" + + "- This hook will be called only for Clusters with a managed topology\n" + + "- The call's request contains the Cluster object, the current Kubernetes version and the Kubernetes version we are upgrading to\n" + + "- This is a blocking hook; Runtime Extension implementers can use this hook to execute " + + "tasks before the upgrade plan continues, or when already at the target spec.topology.version, before AfterClusterUpgrade is called.\n", }) catalogBuilder.RegisterHook(AfterClusterUpgrade, &runtimecatalog.HookMeta{ @@ -266,7 +458,7 @@ func init() { "Notes:\n" + "- This hook will be called only for Clusters with a managed topology\n" + "- The call's request contains the Cluster object and the Kubernetes version we upgraded to \n" + - "- This is a non-blocking hook", + "- This is a blocking hook; Runtime Extension implementers can use this hook to prevent the next upgrade to start.\n", }) catalogBuilder.RegisterHook(BeforeClusterDelete, &runtimecatalog.HookMeta{ diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/topologymutation_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/topologymutation_types.go index 4efa274208..3ea71eae43 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/topologymutation_types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/topologymutation_types.go @@ -101,18 +101,6 @@ type GeneratePatchesResponseItem struct { Patch []byte `json:"patch"` } -// PatchType defines the supported patch types. -// +enum -type PatchType string - -const ( - // JSONPatchType identifies a https://datatracker.ietf.org/doc/html/rfc6902 JSON patch. - JSONPatchType PatchType = "JSONPatch" - - // JSONMergePatchType identifies a https://datatracker.ietf.org/doc/html/rfc7386 JSON merge patch. - JSONMergePatchType PatchType = "JSONMergePatch" -) - // GeneratePatches generates patches during topology reconciliation for the entire Cluster topology. func GeneratePatches(*GeneratePatchesRequest, *GeneratePatchesResponse) {} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/upgrade_plan_types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/upgrade_plan_types.go new file mode 100644 index 0000000000..660e2434aa --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/upgrade_plan_types.go @@ -0,0 +1,129 @@ +/* +Copyright 2025 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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" +) + +// GenerateUpgradePlanRequest is the request of the GenerateUpgradePlan hook. +// +kubebuilder:object:root=true +type GenerateUpgradePlanRequest struct { + metav1.TypeMeta `json:",inline"` + + // CommonRequest contains fields common to all request types. + CommonRequest `json:",inline"` + + // cluster is the cluster object the GenerateUpgradePlan request corresponds to. + // +required + Cluster clusterv1.Cluster `json:"cluster,omitempty,omitzero"` + + // fromControlPlaneKubernetesVersion is the current Kubernetes version of the control plane. + // +required + // +kubebuilder:validation:MinLength=1 + FromControlPlaneKubernetesVersion string `json:"fromControlPlaneKubernetesVersion,omitempty"` + + // fromWorkersKubernetesVersion is the min current Kubernetes version of the workers. + // +optional + // +kubebuilder:validation:MinLength=1 + FromWorkersKubernetesVersion string `json:"fromWorkersKubernetesVersion,omitempty"` + + // toKubernetesVersion is the target Kubernetes version for the upgrade. + // +required + // +kubebuilder:validation:MinLength=1 + ToKubernetesVersion string `json:"toKubernetesVersion,omitempty"` +} + +var _ ResponseObject = &GenerateUpgradePlanResponse{} + +// GenerateUpgradePlanResponse is the response of the GenerateUpgradePlan hook. +// +kubebuilder:object:root=true +type GenerateUpgradePlanResponse struct { + metav1.TypeMeta `json:",inline"` + + // CommonResponse contains Status and Message fields common to all response types. + CommonResponse `json:",inline"` + + // controlPlaneUpgrades is the list of version upgrade steps for the control plane. + // Each entry represents an intermediate version that must be applied in sequence. + // The following rules apply: + // - there should be at least one version for every minor between fromControlPlaneKubernetesVersion (excluded) and ToKubernetesVersion (included). + // - each version must be: + // - greater than fromControlPlaneKubernetesVersion (or with a different build number) + // - greater than the previous version in the list (or with a different build number) + // - less or equal to ToKubernetesVersion (or with a different build number) + // - the last version in the plan must be equal to ToKubernetesVersion + // +optional + ControlPlaneUpgrades []UpgradeStep `json:"controlPlaneUpgrades,omitempty"` + + // workersUpgrades is the list of version upgrade steps for the workers. + // Each entry represents an intermediate version that must be applied in sequence. + // + // In case the upgrade plan for workers will be left to empty, the system will automatically + // determine the minimal number of workers upgrade steps, thus minimizing impact on workloads and reducing + // the overall upgrade time. + // + // If instead for any reason a custom upgrade path for workers is required, the following rules apply: + // - each version must be: + // - equal to FromControlPlaneKubernetesVersion or to one of the versions in the control plane upgrade plan. + // - greater than FromWorkersKubernetesVersion (or with a different build number) + // - greater than the previous version in the list (or with a different build number) + // - less or equal to the ToKubernetesVersion (or with a different build number) + // - in case of versions with the same major/minor/patch version but different build number, also the order + // of those versions must be the same for control plane and worker upgrade plan. + // - the last version in the plan must be equal to ToKubernetesVersion + // - the upgrade plane must have all the intermediate version which workers must go through to avoid breaking rules + // defining the max version skew between control plane and workers. + // +optional + WorkersUpgrades []UpgradeStep `json:"workersUpgrades,omitempty"` +} + +// UpgradeStep represents a single version upgrade step. +type UpgradeStep struct { + // version is the Kubernetes version for this upgrade step. + // +required + // +kubebuilder:validation:MinLength=1 + Version string `json:"version,omitempty"` +} + +// GenerateUpgradePlan is the hook that will be called to generate an upgrade plan +// for a cluster. This hook allows runtime extensions to specify intermediate +// Kubernetes versions that must be applied during an upgrade from the current +// version to the target version. +func GenerateUpgradePlan(*GenerateUpgradePlanRequest, *GenerateUpgradePlanResponse) {} + +func init() { + catalogBuilder.RegisterHook(GenerateUpgradePlan, &runtimecatalog.HookMeta{ + Tags: []string{"Chained Upgrade Hook"}, + Summary: "Cluster API Runtime will call this hook to generate an upgrade plan for a cluster", + Description: "Cluster API Runtime will call this hook to generate an upgrade plan for a cluster. " + + "Runtime Extension implementers can use this hook to specify intermediate Kubernetes versions " + + "that must be applied during an upgrade from the current version to the target version.\n" + + "\n" + + "For example, if upgrading from v1.29.0 to v1.33.0 requires intermediate versions v1.30.0, " + + "v1.31.0, and v1.32.0, the hook should return these intermediate versions in the response.\n" + + "\n" + + "Notes:\n" + + "- The response may include separate upgrade paths for control plane and workers\n" + + "- The upgrade plan for workers is optional; if missing the system will automatically\n\"" + + " determine the minimal number of workers upgrade steps according to Kubernetes version skew rules.\n" + + "- Each upgrade step represents a version that must be applied in sequence", + }) +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/zz_generated.deepcopy.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/zz_generated.deepcopy.go index ce59d1aede..fb8d4012d7 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/zz_generated.deepcopy.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/zz_generated.deepcopy.go @@ -55,7 +55,7 @@ func (in *AfterClusterUpgradeRequest) DeepCopyObject() runtime.Object { func (in *AfterClusterUpgradeResponse) DeepCopyInto(out *AfterClusterUpgradeResponse) { *out = *in out.TypeMeta = in.TypeMeta - out.CommonResponse = in.CommonResponse + out.CommonRetryResponse = in.CommonRetryResponse } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AfterClusterUpgradeResponse. @@ -133,6 +133,16 @@ func (in *AfterControlPlaneUpgradeRequest) DeepCopyInto(out *AfterControlPlaneUp out.TypeMeta = in.TypeMeta in.CommonRequest.DeepCopyInto(&out.CommonRequest) in.Cluster.DeepCopyInto(&out.Cluster) + if in.ControlPlaneUpgrades != nil { + in, out := &in.ControlPlaneUpgrades, &out.ControlPlaneUpgrades + *out = make([]UpgradeStepInfo, len(*in)) + copy(*out, *in) + } + if in.WorkersUpgrades != nil { + in, out := &in.WorkersUpgrades, &out.WorkersUpgrades + *out = make([]UpgradeStepInfo, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AfterControlPlaneUpgradeRequest. @@ -178,6 +188,67 @@ func (in *AfterControlPlaneUpgradeResponse) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AfterWorkersUpgradeRequest) DeepCopyInto(out *AfterWorkersUpgradeRequest) { + *out = *in + out.TypeMeta = in.TypeMeta + in.CommonRequest.DeepCopyInto(&out.CommonRequest) + in.Cluster.DeepCopyInto(&out.Cluster) + if in.ControlPlaneUpgrades != nil { + in, out := &in.ControlPlaneUpgrades, &out.ControlPlaneUpgrades + *out = make([]UpgradeStepInfo, len(*in)) + copy(*out, *in) + } + if in.WorkersUpgrades != nil { + in, out := &in.WorkersUpgrades, &out.WorkersUpgrades + *out = make([]UpgradeStepInfo, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AfterWorkersUpgradeRequest. +func (in *AfterWorkersUpgradeRequest) DeepCopy() *AfterWorkersUpgradeRequest { + if in == nil { + return nil + } + out := new(AfterWorkersUpgradeRequest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AfterWorkersUpgradeRequest) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AfterWorkersUpgradeResponse) DeepCopyInto(out *AfterWorkersUpgradeResponse) { + *out = *in + out.TypeMeta = in.TypeMeta + out.CommonRetryResponse = in.CommonRetryResponse +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AfterWorkersUpgradeResponse. +func (in *AfterWorkersUpgradeResponse) DeepCopy() *AfterWorkersUpgradeResponse { + if in == nil { + return nil + } + out := new(AfterWorkersUpgradeResponse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AfterWorkersUpgradeResponse) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BeforeClusterCreateRequest) DeepCopyInto(out *BeforeClusterCreateRequest) { *out = *in @@ -286,6 +357,16 @@ func (in *BeforeClusterUpgradeRequest) DeepCopyInto(out *BeforeClusterUpgradeReq out.TypeMeta = in.TypeMeta in.CommonRequest.DeepCopyInto(&out.CommonRequest) in.Cluster.DeepCopyInto(&out.Cluster) + if in.ControlPlaneUpgrades != nil { + in, out := &in.ControlPlaneUpgrades, &out.ControlPlaneUpgrades + *out = make([]UpgradeStepInfo, len(*in)) + copy(*out, *in) + } + if in.WorkersUpgrades != nil { + in, out := &in.WorkersUpgrades, &out.WorkersUpgrades + *out = make([]UpgradeStepInfo, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeforeClusterUpgradeRequest. @@ -331,6 +412,128 @@ func (in *BeforeClusterUpgradeResponse) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BeforeControlPlaneUpgradeRequest) DeepCopyInto(out *BeforeControlPlaneUpgradeRequest) { + *out = *in + out.TypeMeta = in.TypeMeta + in.CommonRequest.DeepCopyInto(&out.CommonRequest) + in.Cluster.DeepCopyInto(&out.Cluster) + if in.ControlPlaneUpgrades != nil { + in, out := &in.ControlPlaneUpgrades, &out.ControlPlaneUpgrades + *out = make([]UpgradeStepInfo, len(*in)) + copy(*out, *in) + } + if in.WorkersUpgrades != nil { + in, out := &in.WorkersUpgrades, &out.WorkersUpgrades + *out = make([]UpgradeStepInfo, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeforeControlPlaneUpgradeRequest. +func (in *BeforeControlPlaneUpgradeRequest) DeepCopy() *BeforeControlPlaneUpgradeRequest { + if in == nil { + return nil + } + out := new(BeforeControlPlaneUpgradeRequest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BeforeControlPlaneUpgradeRequest) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BeforeControlPlaneUpgradeResponse) DeepCopyInto(out *BeforeControlPlaneUpgradeResponse) { + *out = *in + out.TypeMeta = in.TypeMeta + out.CommonRetryResponse = in.CommonRetryResponse +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeforeControlPlaneUpgradeResponse. +func (in *BeforeControlPlaneUpgradeResponse) DeepCopy() *BeforeControlPlaneUpgradeResponse { + if in == nil { + return nil + } + out := new(BeforeControlPlaneUpgradeResponse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BeforeControlPlaneUpgradeResponse) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BeforeWorkersUpgradeRequest) DeepCopyInto(out *BeforeWorkersUpgradeRequest) { + *out = *in + out.TypeMeta = in.TypeMeta + in.CommonRequest.DeepCopyInto(&out.CommonRequest) + in.Cluster.DeepCopyInto(&out.Cluster) + if in.ControlPlaneUpgrades != nil { + in, out := &in.ControlPlaneUpgrades, &out.ControlPlaneUpgrades + *out = make([]UpgradeStepInfo, len(*in)) + copy(*out, *in) + } + if in.WorkersUpgrades != nil { + in, out := &in.WorkersUpgrades, &out.WorkersUpgrades + *out = make([]UpgradeStepInfo, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeforeWorkersUpgradeRequest. +func (in *BeforeWorkersUpgradeRequest) DeepCopy() *BeforeWorkersUpgradeRequest { + if in == nil { + return nil + } + out := new(BeforeWorkersUpgradeRequest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BeforeWorkersUpgradeRequest) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BeforeWorkersUpgradeResponse) DeepCopyInto(out *BeforeWorkersUpgradeResponse) { + *out = *in + out.TypeMeta = in.TypeMeta + out.CommonRetryResponse = in.CommonRetryResponse +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeforeWorkersUpgradeResponse. +func (in *BeforeWorkersUpgradeResponse) DeepCopy() *BeforeWorkersUpgradeResponse { + if in == nil { + return nil + } + out := new(BeforeWorkersUpgradeResponse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BeforeWorkersUpgradeResponse) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Builtins) DeepCopyInto(out *Builtins) { *out = *in @@ -366,6 +569,152 @@ func (in *Builtins) DeepCopy() *Builtins { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CanUpdateMachineRequest) DeepCopyInto(out *CanUpdateMachineRequest) { + *out = *in + out.TypeMeta = in.TypeMeta + in.CommonRequest.DeepCopyInto(&out.CommonRequest) + in.Current.DeepCopyInto(&out.Current) + in.Desired.DeepCopyInto(&out.Desired) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CanUpdateMachineRequest. +func (in *CanUpdateMachineRequest) DeepCopy() *CanUpdateMachineRequest { + if in == nil { + return nil + } + out := new(CanUpdateMachineRequest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CanUpdateMachineRequest) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CanUpdateMachineRequestObjects) DeepCopyInto(out *CanUpdateMachineRequestObjects) { + *out = *in + in.Machine.DeepCopyInto(&out.Machine) + in.InfrastructureMachine.DeepCopyInto(&out.InfrastructureMachine) + in.BootstrapConfig.DeepCopyInto(&out.BootstrapConfig) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CanUpdateMachineRequestObjects. +func (in *CanUpdateMachineRequestObjects) DeepCopy() *CanUpdateMachineRequestObjects { + if in == nil { + return nil + } + out := new(CanUpdateMachineRequestObjects) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CanUpdateMachineResponse) DeepCopyInto(out *CanUpdateMachineResponse) { + *out = *in + out.TypeMeta = in.TypeMeta + out.CommonResponse = in.CommonResponse + in.MachinePatch.DeepCopyInto(&out.MachinePatch) + in.InfrastructureMachinePatch.DeepCopyInto(&out.InfrastructureMachinePatch) + in.BootstrapConfigPatch.DeepCopyInto(&out.BootstrapConfigPatch) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CanUpdateMachineResponse. +func (in *CanUpdateMachineResponse) DeepCopy() *CanUpdateMachineResponse { + if in == nil { + return nil + } + out := new(CanUpdateMachineResponse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CanUpdateMachineResponse) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CanUpdateMachineSetRequest) DeepCopyInto(out *CanUpdateMachineSetRequest) { + *out = *in + out.TypeMeta = in.TypeMeta + in.CommonRequest.DeepCopyInto(&out.CommonRequest) + in.Current.DeepCopyInto(&out.Current) + in.Desired.DeepCopyInto(&out.Desired) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CanUpdateMachineSetRequest. +func (in *CanUpdateMachineSetRequest) DeepCopy() *CanUpdateMachineSetRequest { + if in == nil { + return nil + } + out := new(CanUpdateMachineSetRequest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CanUpdateMachineSetRequest) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CanUpdateMachineSetRequestObjects) DeepCopyInto(out *CanUpdateMachineSetRequestObjects) { + *out = *in + in.MachineSet.DeepCopyInto(&out.MachineSet) + in.InfrastructureMachineTemplate.DeepCopyInto(&out.InfrastructureMachineTemplate) + in.BootstrapConfigTemplate.DeepCopyInto(&out.BootstrapConfigTemplate) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CanUpdateMachineSetRequestObjects. +func (in *CanUpdateMachineSetRequestObjects) DeepCopy() *CanUpdateMachineSetRequestObjects { + if in == nil { + return nil + } + out := new(CanUpdateMachineSetRequestObjects) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CanUpdateMachineSetResponse) DeepCopyInto(out *CanUpdateMachineSetResponse) { + *out = *in + out.TypeMeta = in.TypeMeta + out.CommonResponse = in.CommonResponse + in.MachineSetPatch.DeepCopyInto(&out.MachineSetPatch) + in.InfrastructureMachineTemplatePatch.DeepCopyInto(&out.InfrastructureMachineTemplatePatch) + in.BootstrapConfigTemplatePatch.DeepCopyInto(&out.BootstrapConfigTemplatePatch) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CanUpdateMachineSetResponse. +func (in *CanUpdateMachineSetResponse) DeepCopy() *CanUpdateMachineSetResponse { + if in == nil { + return nil + } + out := new(CanUpdateMachineSetResponse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CanUpdateMachineSetResponse) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterBuiltins) DeepCopyInto(out *ClusterBuiltins) { *out = *in @@ -825,6 +1174,67 @@ func (in *GeneratePatchesResponseItem) DeepCopy() *GeneratePatchesResponseItem { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GenerateUpgradePlanRequest) DeepCopyInto(out *GenerateUpgradePlanRequest) { + *out = *in + out.TypeMeta = in.TypeMeta + in.CommonRequest.DeepCopyInto(&out.CommonRequest) + in.Cluster.DeepCopyInto(&out.Cluster) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenerateUpgradePlanRequest. +func (in *GenerateUpgradePlanRequest) DeepCopy() *GenerateUpgradePlanRequest { + if in == nil { + return nil + } + out := new(GenerateUpgradePlanRequest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GenerateUpgradePlanRequest) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GenerateUpgradePlanResponse) DeepCopyInto(out *GenerateUpgradePlanResponse) { + *out = *in + out.TypeMeta = in.TypeMeta + out.CommonResponse = in.CommonResponse + if in.ControlPlaneUpgrades != nil { + in, out := &in.ControlPlaneUpgrades, &out.ControlPlaneUpgrades + *out = make([]UpgradeStep, len(*in)) + copy(*out, *in) + } + if in.WorkersUpgrades != nil { + in, out := &in.WorkersUpgrades, &out.WorkersUpgrades + *out = make([]UpgradeStep, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenerateUpgradePlanResponse. +func (in *GenerateUpgradePlanResponse) DeepCopy() *GenerateUpgradePlanResponse { + if in == nil { + return nil + } + out := new(GenerateUpgradePlanResponse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GenerateUpgradePlanResponse) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GroupVersionHook) DeepCopyInto(out *GroupVersionHook) { *out = *in @@ -975,6 +1385,125 @@ func (in *MachinePoolBuiltins) DeepCopy() *MachinePoolBuiltins { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Patch) DeepCopyInto(out *Patch) { + *out = *in + if in.Patch != nil { + in, out := &in.Patch, &out.Patch + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Patch. +func (in *Patch) DeepCopy() *Patch { + if in == nil { + return nil + } + out := new(Patch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpdateMachineRequest) DeepCopyInto(out *UpdateMachineRequest) { + *out = *in + out.TypeMeta = in.TypeMeta + in.CommonRequest.DeepCopyInto(&out.CommonRequest) + in.Desired.DeepCopyInto(&out.Desired) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateMachineRequest. +func (in *UpdateMachineRequest) DeepCopy() *UpdateMachineRequest { + if in == nil { + return nil + } + out := new(UpdateMachineRequest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *UpdateMachineRequest) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpdateMachineRequestObjects) DeepCopyInto(out *UpdateMachineRequestObjects) { + *out = *in + in.Machine.DeepCopyInto(&out.Machine) + in.InfrastructureMachine.DeepCopyInto(&out.InfrastructureMachine) + in.BootstrapConfig.DeepCopyInto(&out.BootstrapConfig) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateMachineRequestObjects. +func (in *UpdateMachineRequestObjects) DeepCopy() *UpdateMachineRequestObjects { + if in == nil { + return nil + } + out := new(UpdateMachineRequestObjects) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpdateMachineResponse) DeepCopyInto(out *UpdateMachineResponse) { + *out = *in + out.TypeMeta = in.TypeMeta + out.CommonRetryResponse = in.CommonRetryResponse +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateMachineResponse. +func (in *UpdateMachineResponse) DeepCopy() *UpdateMachineResponse { + if in == nil { + return nil + } + out := new(UpdateMachineResponse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *UpdateMachineResponse) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpgradeStep) DeepCopyInto(out *UpgradeStep) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeStep. +func (in *UpgradeStep) DeepCopy() *UpgradeStep { + if in == nil { + return nil + } + out := new(UpgradeStep) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpgradeStepInfo) DeepCopyInto(out *UpgradeStepInfo) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeStepInfo. +func (in *UpgradeStepInfo) DeepCopy() *UpgradeStepInfo { + if in == nil { + return nil + } + out := new(UpgradeStepInfo) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ValidateTopologyRequest) DeepCopyInto(out *ValidateTopologyRequest) { *out = *in diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/zz_generated.openapi.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/zz_generated.openapi.go index e72d124161..787689d73a 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/zz_generated.openapi.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1/zz_generated.openapi.go @@ -34,13 +34,25 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.AfterControlPlaneInitializedResponse": schema_api_runtime_hooks_v1alpha1_AfterControlPlaneInitializedResponse(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.AfterControlPlaneUpgradeRequest": schema_api_runtime_hooks_v1alpha1_AfterControlPlaneUpgradeRequest(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.AfterControlPlaneUpgradeResponse": schema_api_runtime_hooks_v1alpha1_AfterControlPlaneUpgradeResponse(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.AfterWorkersUpgradeRequest": schema_api_runtime_hooks_v1alpha1_AfterWorkersUpgradeRequest(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.AfterWorkersUpgradeResponse": schema_api_runtime_hooks_v1alpha1_AfterWorkersUpgradeResponse(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.BeforeClusterCreateRequest": schema_api_runtime_hooks_v1alpha1_BeforeClusterCreateRequest(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.BeforeClusterCreateResponse": schema_api_runtime_hooks_v1alpha1_BeforeClusterCreateResponse(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.BeforeClusterDeleteRequest": schema_api_runtime_hooks_v1alpha1_BeforeClusterDeleteRequest(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.BeforeClusterDeleteResponse": schema_api_runtime_hooks_v1alpha1_BeforeClusterDeleteResponse(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.BeforeClusterUpgradeRequest": schema_api_runtime_hooks_v1alpha1_BeforeClusterUpgradeRequest(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.BeforeClusterUpgradeResponse": schema_api_runtime_hooks_v1alpha1_BeforeClusterUpgradeResponse(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.BeforeControlPlaneUpgradeRequest": schema_api_runtime_hooks_v1alpha1_BeforeControlPlaneUpgradeRequest(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.BeforeControlPlaneUpgradeResponse": schema_api_runtime_hooks_v1alpha1_BeforeControlPlaneUpgradeResponse(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.BeforeWorkersUpgradeRequest": schema_api_runtime_hooks_v1alpha1_BeforeWorkersUpgradeRequest(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.BeforeWorkersUpgradeResponse": schema_api_runtime_hooks_v1alpha1_BeforeWorkersUpgradeResponse(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Builtins": schema_api_runtime_hooks_v1alpha1_Builtins(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.CanUpdateMachineRequest": schema_api_runtime_hooks_v1alpha1_CanUpdateMachineRequest(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.CanUpdateMachineRequestObjects": schema_api_runtime_hooks_v1alpha1_CanUpdateMachineRequestObjects(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.CanUpdateMachineResponse": schema_api_runtime_hooks_v1alpha1_CanUpdateMachineResponse(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.CanUpdateMachineSetRequest": schema_api_runtime_hooks_v1alpha1_CanUpdateMachineSetRequest(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.CanUpdateMachineSetRequestObjects": schema_api_runtime_hooks_v1alpha1_CanUpdateMachineSetRequestObjects(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.CanUpdateMachineSetResponse": schema_api_runtime_hooks_v1alpha1_CanUpdateMachineSetResponse(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterBuiltins": schema_api_runtime_hooks_v1alpha1_ClusterBuiltins(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterNetworkBuiltins": schema_api_runtime_hooks_v1alpha1_ClusterNetworkBuiltins(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterTopologyBuiltins": schema_api_runtime_hooks_v1alpha1_ClusterTopologyBuiltins(ref), @@ -60,6 +72,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.GeneratePatchesRequestItem": schema_api_runtime_hooks_v1alpha1_GeneratePatchesRequestItem(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.GeneratePatchesResponse": schema_api_runtime_hooks_v1alpha1_GeneratePatchesResponse(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.GeneratePatchesResponseItem": schema_api_runtime_hooks_v1alpha1_GeneratePatchesResponseItem(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.GenerateUpgradePlanRequest": schema_api_runtime_hooks_v1alpha1_GenerateUpgradePlanRequest(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.GenerateUpgradePlanResponse": schema_api_runtime_hooks_v1alpha1_GenerateUpgradePlanResponse(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.GroupVersionHook": schema_api_runtime_hooks_v1alpha1_GroupVersionHook(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.HolderReference": schema_api_runtime_hooks_v1alpha1_HolderReference(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.MachineBootstrapBuiltins": schema_api_runtime_hooks_v1alpha1_MachineBootstrapBuiltins(ref), @@ -67,6 +81,12 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.MachineDeploymentBuiltins": schema_api_runtime_hooks_v1alpha1_MachineDeploymentBuiltins(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.MachineInfrastructureRefBuiltins": schema_api_runtime_hooks_v1alpha1_MachineInfrastructureRefBuiltins(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.MachinePoolBuiltins": schema_api_runtime_hooks_v1alpha1_MachinePoolBuiltins(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Patch": schema_api_runtime_hooks_v1alpha1_Patch(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpdateMachineRequest": schema_api_runtime_hooks_v1alpha1_UpdateMachineRequest(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpdateMachineRequestObjects": schema_api_runtime_hooks_v1alpha1_UpdateMachineRequestObjects(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpdateMachineResponse": schema_api_runtime_hooks_v1alpha1_UpdateMachineResponse(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStep": schema_api_runtime_hooks_v1alpha1_UpgradeStep(ref), + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo": schema_api_runtime_hooks_v1alpha1_UpgradeStepInfo(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ValidateTopologyRequest": schema_api_runtime_hooks_v1alpha1_ValidateTopologyRequest(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ValidateTopologyRequestItem": schema_api_runtime_hooks_v1alpha1_ValidateTopologyRequestItem(ref), "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ValidateTopologyResponse": schema_api_runtime_hooks_v1alpha1_ValidateTopologyResponse(ref), @@ -172,8 +192,16 @@ func schema_api_runtime_hooks_v1alpha1_AfterClusterUpgradeResponse(ref common.Re Format: "", }, }, + "retryAfterSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "retryAfterSeconds when set to a non-zero value signifies that the hook will be called again at a future time.", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, }, - Required: []string{"status"}, + Required: []string{"status", "retryAfterSeconds"}, }, }, } @@ -322,18 +350,46 @@ func schema_api_runtime_hooks_v1alpha1_AfterControlPlaneUpgradeRequest(ref commo }, "kubernetesVersion": { SchemaProps: spec.SchemaProps{ - Description: "kubernetesVersion is the Kubernetes version of the Control Plane after the upgrade.", + Description: "kubernetesVersion is the Kubernetes version of the control plane after an upgrade step.", Default: "", Type: []string{"string"}, Format: "", }, }, + "controlPlaneUpgrades": { + SchemaProps: spec.SchemaProps{ + Description: "controlPlaneUpgrades is the list of the remaining version upgrade steps for the control plane, if any.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"), + }, + }, + }, + }, + }, + "workersUpgrades": { + SchemaProps: spec.SchemaProps{ + Description: "workersUpgrades is the list of the remaining version upgrade steps for workers, if any.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"), + }, + }, + }, + }, + }, }, Required: []string{"cluster", "kubernetesVersion"}, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta1.Cluster"}, + "sigs.k8s.io/cluster-api/api/core/v1beta1.Cluster", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"}, } } @@ -389,11 +445,11 @@ func schema_api_runtime_hooks_v1alpha1_AfterControlPlaneUpgradeResponse(ref comm } } -func schema_api_runtime_hooks_v1alpha1_BeforeClusterCreateRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_api_runtime_hooks_v1alpha1_AfterWorkersUpgradeRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "BeforeClusterCreateRequest is the request of the BeforeClusterCreate hook.", + Description: "AfterWorkersUpgradeRequest is the request of the AfterWorkersUpgrade hook.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -433,20 +489,56 @@ func schema_api_runtime_hooks_v1alpha1_BeforeClusterCreateRequest(ref common.Ref Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta1.Cluster"), }, }, + "kubernetesVersion": { + SchemaProps: spec.SchemaProps{ + Description: "kubernetesVersion is the Kubernetes version of the workers after an upgrade step.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "controlPlaneUpgrades": { + SchemaProps: spec.SchemaProps{ + Description: "controlPlaneUpgrades is the list of the remaining version upgrade steps for the control plane, if any.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"), + }, + }, + }, + }, + }, + "workersUpgrades": { + SchemaProps: spec.SchemaProps{ + Description: "workersUpgrades is the list of the remaining version upgrade steps for workers, if any.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"), + }, + }, + }, + }, + }, }, - Required: []string{"cluster"}, + Required: []string{"cluster", "kubernetesVersion"}, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta1.Cluster"}, + "sigs.k8s.io/cluster-api/api/core/v1beta1.Cluster", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"}, } } -func schema_api_runtime_hooks_v1alpha1_BeforeClusterCreateResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_api_runtime_hooks_v1alpha1_AfterWorkersUpgradeResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "BeforeClusterCreateResponse is the response of the BeforeClusterCreate hook.", + Description: "AfterWorkersUpgradeResponse is the response of the AfterWorkersUpgrade hook.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -494,11 +586,11 @@ func schema_api_runtime_hooks_v1alpha1_BeforeClusterCreateResponse(ref common.Re } } -func schema_api_runtime_hooks_v1alpha1_BeforeClusterDeleteRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_api_runtime_hooks_v1alpha1_BeforeClusterCreateRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "BeforeClusterDeleteRequest is the request of the BeforeClusterDelete hook.", + Description: "BeforeClusterCreateRequest is the request of the BeforeClusterCreate hook.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -547,11 +639,11 @@ func schema_api_runtime_hooks_v1alpha1_BeforeClusterDeleteRequest(ref common.Ref } } -func schema_api_runtime_hooks_v1alpha1_BeforeClusterDeleteResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_api_runtime_hooks_v1alpha1_BeforeClusterCreateResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "BeforeClusterDeleteResponse is the response of the BeforeClusterDelete hook.", + Description: "BeforeClusterCreateResponse is the response of the BeforeClusterCreate hook.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -599,11 +691,11 @@ func schema_api_runtime_hooks_v1alpha1_BeforeClusterDeleteResponse(ref common.Re } } -func schema_api_runtime_hooks_v1alpha1_BeforeClusterUpgradeRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_api_runtime_hooks_v1alpha1_BeforeClusterDeleteRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "BeforeClusterUpgradeRequest is the request of the BeforeClusterUpgrade hook.", + Description: "BeforeClusterDeleteRequest is the request of the BeforeClusterDelete hook.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -643,24 +735,8 @@ func schema_api_runtime_hooks_v1alpha1_BeforeClusterUpgradeRequest(ref common.Re Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta1.Cluster"), }, }, - "fromKubernetesVersion": { - SchemaProps: spec.SchemaProps{ - Description: "fromKubernetesVersion is the current Kubernetes version of the cluster.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "toKubernetesVersion": { - SchemaProps: spec.SchemaProps{ - Description: "toKubernetesVersion is the target Kubernetes version of the upgrade.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, }, - Required: []string{"cluster", "fromKubernetesVersion", "toKubernetesVersion"}, + Required: []string{"cluster"}, }, }, Dependencies: []string{ @@ -668,11 +744,11 @@ func schema_api_runtime_hooks_v1alpha1_BeforeClusterUpgradeRequest(ref common.Re } } -func schema_api_runtime_hooks_v1alpha1_BeforeClusterUpgradeResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_api_runtime_hooks_v1alpha1_BeforeClusterDeleteResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "BeforeClusterUpgradeResponse is the response of the BeforeClusterUpgrade hook.", + Description: "BeforeClusterDeleteResponse is the response of the BeforeClusterDelete hook.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -720,133 +796,182 @@ func schema_api_runtime_hooks_v1alpha1_BeforeClusterUpgradeResponse(ref common.R } } -func schema_api_runtime_hooks_v1alpha1_Builtins(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_api_runtime_hooks_v1alpha1_BeforeClusterUpgradeRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Builtins represents builtin variables exposed through patches.", + Description: "BeforeClusterUpgradeRequest is the request of the BeforeClusterUpgrade hook.", Type: []string{"object"}, Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "settings": { + SchemaProps: spec.SchemaProps{ + Description: "settings defines key value pairs to be passed to the call.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, "cluster": { SchemaProps: spec.SchemaProps{ - Description: "cluster represents builtin cluster variables.", - Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterBuiltins"), + Description: "cluster is the cluster object the lifecycle hook corresponds to.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta1.Cluster"), }, }, - "controlPlane": { + "fromKubernetesVersion": { SchemaProps: spec.SchemaProps{ - Description: "controlPlane represents builtin ControlPlane variables.", - Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ControlPlaneBuiltins"), + Description: "fromKubernetesVersion is the current Kubernetes version of the cluster.", + Default: "", + Type: []string{"string"}, + Format: "", }, }, - "machineDeployment": { + "toKubernetesVersion": { SchemaProps: spec.SchemaProps{ - Description: "machineDeployment represents builtin MachineDeployment variables.", - Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.MachineDeploymentBuiltins"), + Description: "toKubernetesVersion is the target Kubernetes version of the upgrade.", + Default: "", + Type: []string{"string"}, + Format: "", }, }, - "machinePool": { + "controlPlaneUpgrades": { SchemaProps: spec.SchemaProps{ - Description: "machinePool represents builtin MachinePool variables.", - Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.MachinePoolBuiltins"), + Description: "controlPlaneUpgrades is the list of version upgrade steps for the control plane.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"), + }, + }, + }, + }, + }, + "workersUpgrades": { + SchemaProps: spec.SchemaProps{ + Description: "workersUpgrades is the list of version upgrade steps for the workers.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"), + }, + }, + }, }, }, }, + Required: []string{"cluster", "fromKubernetesVersion", "toKubernetesVersion"}, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterBuiltins", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ControlPlaneBuiltins", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.MachineDeploymentBuiltins", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.MachinePoolBuiltins"}, + "sigs.k8s.io/cluster-api/api/core/v1beta1.Cluster", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"}, } } -func schema_api_runtime_hooks_v1alpha1_ClusterBuiltins(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_api_runtime_hooks_v1alpha1_BeforeClusterUpgradeResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "ClusterBuiltins represents builtin cluster variables.", + Description: "BeforeClusterUpgradeResponse is the response of the BeforeClusterUpgrade hook.", Type: []string{"object"}, Properties: map[string]spec.Schema{ - "name": { + "kind": { SchemaProps: spec.SchemaProps{ - Description: "name is the name of the cluster.", + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", Type: []string{"string"}, Format: "", }, }, - "namespace": { + "apiVersion": { SchemaProps: spec.SchemaProps{ - Description: "namespace is the namespace of the cluster.", + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", Type: []string{"string"}, Format: "", }, }, - "uid": { + "status": { SchemaProps: spec.SchemaProps{ - Description: "uid is the unqiue identifier of the cluster.", + Description: "status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.", + Default: "", Type: []string{"string"}, Format: "", + Enum: []interface{}{"Failure", "Success"}, }, }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "metadata is the metadata set on the Cluster object.", - Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta1.ObjectMeta"), - }, - }, - "topology": { + "message": { SchemaProps: spec.SchemaProps{ - Description: "topology represents the cluster topology variables.", - Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterTopologyBuiltins"), + Description: "message is a human-readable description of the status of the call.", + Type: []string{"string"}, + Format: "", }, }, - "network": { + "retryAfterSeconds": { SchemaProps: spec.SchemaProps{ - Description: "network represents the cluster network variables.", - Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterNetworkBuiltins"), + Description: "retryAfterSeconds when set to a non-zero value signifies that the hook will be called again at a future time.", + Default: 0, + Type: []string{"integer"}, + Format: "int32", }, }, }, + Required: []string{"status", "retryAfterSeconds"}, }, }, - Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta1.ObjectMeta", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterNetworkBuiltins", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterTopologyBuiltins"}, } } -func schema_api_runtime_hooks_v1alpha1_ClusterNetworkBuiltins(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_api_runtime_hooks_v1alpha1_BeforeControlPlaneUpgradeRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "ClusterNetworkBuiltins represents builtin cluster network variables.", + Description: "BeforeControlPlaneUpgradeRequest is the request of the BeforeControlPlane hook.", Type: []string{"object"}, Properties: map[string]spec.Schema{ - "serviceDomain": { + "kind": { SchemaProps: spec.SchemaProps{ - Description: "serviceDomain is the domain name for services.", + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", Type: []string{"string"}, Format: "", }, }, - "services": { + "apiVersion": { SchemaProps: spec.SchemaProps{ - Description: "services is the network ranges from which service VIPs are allocated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", }, }, - "pods": { + "settings": { SchemaProps: spec.SchemaProps{ - Description: "pods is the network ranges from which Pod networks are allocated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ + Description: "settings defines key value pairs to be passed to the call.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: "", @@ -857,7 +982,729 @@ func schema_api_runtime_hooks_v1alpha1_ClusterNetworkBuiltins(ref common.Referen }, }, }, - }, + "cluster": { + SchemaProps: spec.SchemaProps{ + Description: "cluster is the cluster object the lifecycle hook corresponds to.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta1.Cluster"), + }, + }, + "fromKubernetesVersion": { + SchemaProps: spec.SchemaProps{ + Description: "fromKubernetesVersion is the current Kubernetes version of the control plane for the next upgrade step.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "toKubernetesVersion": { + SchemaProps: spec.SchemaProps{ + Description: "toKubernetesVersion is the target Kubernetes version of the control plane for the next upgrade step.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "controlPlaneUpgrades": { + SchemaProps: spec.SchemaProps{ + Description: "controlPlaneUpgrades is the list of the remaining version upgrade steps for the control plane, if any.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"), + }, + }, + }, + }, + }, + "workersUpgrades": { + SchemaProps: spec.SchemaProps{ + Description: "workersUpgrades is the list of the remaining version upgrade steps for workers, if any.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"), + }, + }, + }, + }, + }, + }, + Required: []string{"cluster", "fromKubernetesVersion", "toKubernetesVersion"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/core/v1beta1.Cluster", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_BeforeControlPlaneUpgradeResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BeforeControlPlaneUpgradeResponse is the response of the BeforeControlPlaneUpgrade hook.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.", + Default: "", + Type: []string{"string"}, + Format: "", + Enum: []interface{}{"Failure", "Success"}, + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "message is a human-readable description of the status of the call.", + Type: []string{"string"}, + Format: "", + }, + }, + "retryAfterSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "retryAfterSeconds when set to a non-zero value signifies that the hook will be called again at a future time.", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"status", "retryAfterSeconds"}, + }, + }, + } +} + +func schema_api_runtime_hooks_v1alpha1_BeforeWorkersUpgradeRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BeforeWorkersUpgradeRequest is the request of the BeforeWorkersUpgrade hook.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "settings": { + SchemaProps: spec.SchemaProps{ + Description: "settings defines key value pairs to be passed to the call.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "cluster": { + SchemaProps: spec.SchemaProps{ + Description: "cluster is the cluster object the lifecycle hook corresponds to.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta1.Cluster"), + }, + }, + "fromKubernetesVersion": { + SchemaProps: spec.SchemaProps{ + Description: "fromKubernetesVersion is the current Kubernetes version of the workers for the next upgrade step.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "toKubernetesVersion": { + SchemaProps: spec.SchemaProps{ + Description: "toKubernetesVersion is the target Kubernetes version of the workers for the next upgrade step.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "controlPlaneUpgrades": { + SchemaProps: spec.SchemaProps{ + Description: "controlPlaneUpgrades is the list of the remaining version upgrade steps for the control plane, if any.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"), + }, + }, + }, + }, + }, + "workersUpgrades": { + SchemaProps: spec.SchemaProps{ + Description: "workersUpgrades is the list of the remaining version upgrade steps for workers, if any.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"), + }, + }, + }, + }, + }, + }, + Required: []string{"cluster", "fromKubernetesVersion", "toKubernetesVersion"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/core/v1beta1.Cluster", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStepInfo"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_BeforeWorkersUpgradeResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BeforeWorkersUpgradeResponse is the response of the BeforeWorkersUpgrade hook.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.", + Default: "", + Type: []string{"string"}, + Format: "", + Enum: []interface{}{"Failure", "Success"}, + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "message is a human-readable description of the status of the call.", + Type: []string{"string"}, + Format: "", + }, + }, + "retryAfterSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "retryAfterSeconds when set to a non-zero value signifies that the hook will be called again at a future time.", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"status", "retryAfterSeconds"}, + }, + }, + } +} + +func schema_api_runtime_hooks_v1alpha1_Builtins(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Builtins represents builtin variables exposed through patches.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "cluster": { + SchemaProps: spec.SchemaProps{ + Description: "cluster represents builtin cluster variables.", + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterBuiltins"), + }, + }, + "controlPlane": { + SchemaProps: spec.SchemaProps{ + Description: "controlPlane represents builtin ControlPlane variables.", + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ControlPlaneBuiltins"), + }, + }, + "machineDeployment": { + SchemaProps: spec.SchemaProps{ + Description: "machineDeployment represents builtin MachineDeployment variables.", + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.MachineDeploymentBuiltins"), + }, + }, + "machinePool": { + SchemaProps: spec.SchemaProps{ + Description: "machinePool represents builtin MachinePool variables.", + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.MachinePoolBuiltins"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterBuiltins", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ControlPlaneBuiltins", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.MachineDeploymentBuiltins", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.MachinePoolBuiltins"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_CanUpdateMachineRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CanUpdateMachineRequest is the request of the CanUpdateMachine hook.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "settings": { + SchemaProps: spec.SchemaProps{ + Description: "settings defines key value pairs to be passed to the call.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "current": { + SchemaProps: spec.SchemaProps{ + Description: "current contains the current state of the Machine and related objects.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.CanUpdateMachineRequestObjects"), + }, + }, + "desired": { + SchemaProps: spec.SchemaProps{ + Description: "desired contains the desired state of the Machine and related objects.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.CanUpdateMachineRequestObjects"), + }, + }, + }, + Required: []string{"current", "desired"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.CanUpdateMachineRequestObjects"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_CanUpdateMachineRequestObjects(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CanUpdateMachineRequestObjects groups objects for CanUpdateMachineRequest.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "machine": { + SchemaProps: spec.SchemaProps{ + Description: "machine is the full Machine object.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.Machine"), + }, + }, + "infrastructureMachine": { + SchemaProps: spec.SchemaProps{ + Description: "infrastructureMachine is the infra Machine object.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + "bootstrapConfig": { + SchemaProps: spec.SchemaProps{ + Description: "bootstrapConfig is the bootstrap config object.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + }, + Required: []string{"machine", "infrastructureMachine"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/runtime.RawExtension", "sigs.k8s.io/cluster-api/api/core/v1beta2.Machine"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_CanUpdateMachineResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CanUpdateMachineResponse is the response of the CanUpdateMachine hook.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.", + Default: "", + Type: []string{"string"}, + Format: "", + Enum: []interface{}{"Failure", "Success"}, + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "message is a human-readable description of the status of the call.", + Type: []string{"string"}, + Format: "", + }, + }, + "machinePatch": { + SchemaProps: spec.SchemaProps{ + Description: "machinePatch when applied to the current Machine spec, indicates changes handled in-place. Only fields in spec have to be covered by the patch.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Patch"), + }, + }, + "infrastructureMachinePatch": { + SchemaProps: spec.SchemaProps{ + Description: "infrastructureMachinePatch indicates infra Machine spec changes handled in-place. Only fields in spec have to be covered by the patch.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Patch"), + }, + }, + "bootstrapConfigPatch": { + SchemaProps: spec.SchemaProps{ + Description: "bootstrapConfigPatch indicates bootstrap config spec changes handled in-place. Only fields in spec have to be covered by the patch.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Patch"), + }, + }, + }, + Required: []string{"status"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Patch"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_CanUpdateMachineSetRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CanUpdateMachineSetRequest is the request of the CanUpdateMachineSet hook.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "settings": { + SchemaProps: spec.SchemaProps{ + Description: "settings defines key value pairs to be passed to the call.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "current": { + SchemaProps: spec.SchemaProps{ + Description: "current contains the current state of the MachineSet and related objects.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.CanUpdateMachineSetRequestObjects"), + }, + }, + "desired": { + SchemaProps: spec.SchemaProps{ + Description: "desired contains the desired state of the MachineSet and related objects.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.CanUpdateMachineSetRequestObjects"), + }, + }, + }, + Required: []string{"current", "desired"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.CanUpdateMachineSetRequestObjects"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_CanUpdateMachineSetRequestObjects(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CanUpdateMachineSetRequestObjects groups objects for CanUpdateMachineSetRequest.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "machineSet": { + SchemaProps: spec.SchemaProps{ + Description: "machineSet is the full MachineSet object. Only fields in spec.template.spec have to be covered by the patch.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.MachineSet"), + }, + }, + "infrastructureMachineTemplate": { + SchemaProps: spec.SchemaProps{ + Description: "infrastructureMachineTemplate is the provider-specific InfrastructureMachineTemplate object. Only fields in spec.template.spec have to be covered by the patch.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + "bootstrapConfigTemplate": { + SchemaProps: spec.SchemaProps{ + Description: "bootstrapConfigTemplate is the provider-specific BootstrapConfigTemplate object. Only fields in spec.template.spec have to be covered by the patch.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + }, + Required: []string{"machineSet", "infrastructureMachineTemplate"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/runtime.RawExtension", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineSet"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_CanUpdateMachineSetResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CanUpdateMachineSetResponse is the response of the CanUpdateMachineSet hook.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.", + Default: "", + Type: []string{"string"}, + Format: "", + Enum: []interface{}{"Failure", "Success"}, + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "message is a human-readable description of the status of the call.", + Type: []string{"string"}, + Format: "", + }, + }, + "machineSetPatch": { + SchemaProps: spec.SchemaProps{ + Description: "machineSetPatch when applied to the current MachineSet spec, indicates changes handled in-place.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Patch"), + }, + }, + "infrastructureMachineTemplatePatch": { + SchemaProps: spec.SchemaProps{ + Description: "infrastructureMachineTemplatePatch indicates infra template spec changes handled in-place.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Patch"), + }, + }, + "bootstrapConfigTemplatePatch": { + SchemaProps: spec.SchemaProps{ + Description: "bootstrapConfigTemplatePatch indicates bootstrap template spec changes handled in-place.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Patch"), + }, + }, + }, + Required: []string{"status"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Patch"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_ClusterBuiltins(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterBuiltins represents builtin cluster variables.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is the name of the cluster.", + Type: []string{"string"}, + Format: "", + }, + }, + "namespace": { + SchemaProps: spec.SchemaProps{ + Description: "namespace is the namespace of the cluster.", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "uid is the unqiue identifier of the cluster.", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "metadata is the metadata set on the Cluster object.", + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta1.ObjectMeta"), + }, + }, + "topology": { + SchemaProps: spec.SchemaProps{ + Description: "topology represents the cluster topology variables.", + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterTopologyBuiltins"), + }, + }, + "network": { + SchemaProps: spec.SchemaProps{ + Description: "network represents the cluster network variables.", + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterNetworkBuiltins"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/core/v1beta1.ObjectMeta", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterNetworkBuiltins", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.ClusterTopologyBuiltins"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_ClusterNetworkBuiltins(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterNetworkBuiltins represents builtin cluster network variables.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "serviceDomain": { + SchemaProps: spec.SchemaProps{ + Description: "serviceDomain is the domain name for services.", + Type: []string{"string"}, + Format: "", + }, + }, + "services": { + SchemaProps: spec.SchemaProps{ + Description: "services is the network ranges from which service VIPs are allocated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "pods": { + SchemaProps: spec.SchemaProps{ + Description: "pods is the network ranges from which Pod networks are allocated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, }, }, } @@ -1456,47 +2303,218 @@ func schema_api_runtime_hooks_v1alpha1_GeneratePatchesRequestItem(ref common.Ref Format: "", }, }, - "holderReference": { + "holderReference": { + SchemaProps: spec.SchemaProps{ + Description: "holderReference is a reference to the object where the template is used.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.HolderReference"), + }, + }, + "object": { + SchemaProps: spec.SchemaProps{ + Description: "object contains the template as a raw object.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + "variables": { + SchemaProps: spec.SchemaProps{ + Description: "variables are variables specific for the current template. For example some builtin variables like MachineDeployment replicas and version are context-sensitive and thus are only added to templates for MachineDeployments and with values which correspond to the current MachineDeployment.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Variable"), + }, + }, + }, + }, + }, + }, + Required: []string{"uid", "holderReference", "object"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/runtime.RawExtension", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.HolderReference", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Variable"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_GeneratePatchesResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GeneratePatchesResponse is the response of the GeneratePatches hook. NOTE: The patches in GeneratePatchesResponse will be applied in the order in which they are defined to the templates of the request. Thus applying changes consecutively when iterating through internal and external patches.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.", + Default: "", + Type: []string{"string"}, + Format: "", + Enum: []interface{}{"Failure", "Success"}, + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "message is a human-readable description of the status of the call.", + Type: []string{"string"}, + Format: "", + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "items is the list of generated patches.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.GeneratePatchesResponseItem"), + }, + }, + }, + }, + }, + }, + Required: []string{"status"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.GeneratePatchesResponseItem"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_GeneratePatchesResponseItem(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GeneratePatchesResponseItem is a generated patch.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "uid identifies the corresponding template in the request on which the patch should be applied.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "patchType": { + SchemaProps: spec.SchemaProps{ + Description: "patchType defines the type of the patch. One of: \"JSONPatch\" or \"JSONMergePatch\".", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "patch": { + SchemaProps: spec.SchemaProps{ + Description: "patch contains the patch which should be applied to the template. It must be of the corresponding PatchType.", + Type: []string{"string"}, + Format: "byte", + }, + }, + }, + Required: []string{"uid", "patchType", "patch"}, + }, + }, + } +} + +func schema_api_runtime_hooks_v1alpha1_GenerateUpgradePlanRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GenerateUpgradePlanRequest is the request of the GenerateUpgradePlan hook.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "settings": { + SchemaProps: spec.SchemaProps{ + Description: "settings defines key value pairs to be passed to the call.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "cluster": { + SchemaProps: spec.SchemaProps{ + Description: "cluster is the cluster object the GenerateUpgradePlan request corresponds to.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.Cluster"), + }, + }, + "fromControlPlaneKubernetesVersion": { SchemaProps: spec.SchemaProps{ - Description: "holderReference is a reference to the object where the template is used.", - Default: map[string]interface{}{}, - Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.HolderReference"), + Description: "fromControlPlaneKubernetesVersion is the current Kubernetes version of the control plane.", + Type: []string{"string"}, + Format: "", }, }, - "object": { + "fromWorkersKubernetesVersion": { SchemaProps: spec.SchemaProps{ - Description: "object contains the template as a raw object.", - Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + Description: "fromWorkersKubernetesVersion is the min current Kubernetes version of the workers.", + Type: []string{"string"}, + Format: "", }, }, - "variables": { + "toKubernetesVersion": { SchemaProps: spec.SchemaProps{ - Description: "variables are variables specific for the current template. For example some builtin variables like MachineDeployment replicas and version are context-sensitive and thus are only added to templates for MachineDeployments and with values which correspond to the current MachineDeployment.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Variable"), - }, - }, - }, + Description: "toKubernetesVersion is the target Kubernetes version for the upgrade.", + Type: []string{"string"}, + Format: "", }, }, }, - Required: []string{"uid", "holderReference", "object"}, + Required: []string{"cluster", "fromControlPlaneKubernetesVersion", "toKubernetesVersion"}, }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/runtime.RawExtension", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.HolderReference", "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.Variable"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.Cluster"}, } } -func schema_api_runtime_hooks_v1alpha1_GeneratePatchesResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_api_runtime_hooks_v1alpha1_GenerateUpgradePlanResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "GeneratePatchesResponse is the response of the GeneratePatches hook. NOTE: The patches in GeneratePatchesResponse will be applied in the order in which they are defined to the templates of the request. Thus applying changes consecutively when iterating through internal and external patches.", + Description: "GenerateUpgradePlanResponse is the response of the GenerateUpgradePlan hook.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -1529,64 +2547,40 @@ func schema_api_runtime_hooks_v1alpha1_GeneratePatchesResponse(ref common.Refere Format: "", }, }, - "items": { + "controlPlaneUpgrades": { SchemaProps: spec.SchemaProps{ - Description: "items is the list of generated patches.", + Description: "controlPlaneUpgrades is the list of version upgrade steps for the control plane. Each entry represents an intermediate version that must be applied in sequence. The following rules apply: - there should be at least one version for every minor between \t\tfromControlPlaneKubernetesVersion (excluded) and ToKubernetesVersion (included). - each version must be:\n - greater than fromControlPlaneKubernetesVersion (or with a different build \tnumber)\n - greater than the previous version in the list (or with a different build number)\n - less or equal to ToKubernetesVersion (or with a different build number)\n - the last version in the plan must be equal to ToKubernetesVersion", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.GeneratePatchesResponseItem"), + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStep"), }, }, }, }, }, - }, - Required: []string{"status"}, - }, - }, - Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.GeneratePatchesResponseItem"}, - } -} - -func schema_api_runtime_hooks_v1alpha1_GeneratePatchesResponseItem(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "GeneratePatchesResponseItem is a generated patch.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "uid": { - SchemaProps: spec.SchemaProps{ - Description: "uid identifies the corresponding template in the request on which the patch should be applied.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "patchType": { - SchemaProps: spec.SchemaProps{ - Description: "patchType defines the type of the patch. One of: \"JSONPatch\" or \"JSONMergePatch\".\n\nPossible enum values:\n - `\"JSONMergePatch\"` identifies a https://datatracker.ietf.org/doc/html/rfc7386 JSON merge patch.\n - `\"JSONPatch\"` identifies a https://datatracker.ietf.org/doc/html/rfc6902 JSON patch.", - Default: "", - Type: []string{"string"}, - Format: "", - Enum: []interface{}{"JSONMergePatch", "JSONPatch"}, - }, - }, - "patch": { + "workersUpgrades": { SchemaProps: spec.SchemaProps{ - Description: "patch contains the patch which should be applied to the template. It must be of the corresponding PatchType.", - Type: []string{"string"}, - Format: "byte", + Description: "workersUpgrades is the list of version upgrade steps for the workers. Each entry represents an intermediate version that must be applied in sequence.\n\nIn case the upgrade plan for workers will be left to empty, the system will automatically determine the minimal number of workers upgrade steps, thus minimizing impact on workloads and reducing the overall upgrade time.\n\nIf instead for any reason a custom upgrade path for workers is required, the following rules apply: - each version must be:\n - equal to FromControlPlaneKubernetesVersion or to one of the versions in the control plane upgrade plan.\n - greater than FromWorkersKubernetesVersion (or with a different build number)\n - greater than the previous version in the list (or with a different build number)\n - less or equal to the ToKubernetesVersion (or with a different build number)\n - in case of versions with the same major/minor/patch version but different build number, also the order\n of those versions must be the same for control plane and worker upgrade plan.\n - the last version in the plan must be equal to ToKubernetesVersion\n - the upgrade plane must have all the intermediate version which workers must go through to avoid breaking rules\n defining the max version skew between control plane and workers.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStep"), + }, + }, + }, }, }, }, - Required: []string{"uid", "patchType", "patch"}, + Required: []string{"status"}, }, }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpgradeStep"}, } } @@ -1871,6 +2865,216 @@ func schema_api_runtime_hooks_v1alpha1_MachinePoolBuiltins(ref common.ReferenceC } } +func schema_api_runtime_hooks_v1alpha1_Patch(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Patch is a single patch (JSONPatch or JSONMergePatch) which can include multiple operations.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "patchType": { + SchemaProps: spec.SchemaProps{ + Description: "patchType JSONPatch or JSONMergePatch.", + Type: []string{"string"}, + Format: "", + }, + }, + "patch": { + SchemaProps: spec.SchemaProps{ + Description: "patch data for the target object.", + Type: []string{"string"}, + Format: "byte", + }, + }, + }, + Required: []string{"patchType", "patch"}, + }, + }, + } +} + +func schema_api_runtime_hooks_v1alpha1_UpdateMachineRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UpdateMachineRequest is the request of the UpdateMachine hook.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "settings": { + SchemaProps: spec.SchemaProps{ + Description: "settings defines key value pairs to be passed to the call.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "desired": { + SchemaProps: spec.SchemaProps{ + Description: "desired contains the desired state of the Machine and related objects.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpdateMachineRequestObjects"), + }, + }, + }, + Required: []string{"desired"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.UpdateMachineRequestObjects"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_UpdateMachineRequestObjects(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UpdateMachineRequestObjects groups objects for UpdateMachineRequest.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "machine": { + SchemaProps: spec.SchemaProps{ + Description: "machine is the full Machine object.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.Machine"), + }, + }, + "infrastructureMachine": { + SchemaProps: spec.SchemaProps{ + Description: "infrastructureMachine is the infra Machine object.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + "bootstrapConfig": { + SchemaProps: spec.SchemaProps{ + Description: "bootstrapConfig is the bootstrap config object.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + }, + Required: []string{"machine", "infrastructureMachine"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/runtime.RawExtension", "sigs.k8s.io/cluster-api/api/core/v1beta2.Machine"}, + } +} + +func schema_api_runtime_hooks_v1alpha1_UpdateMachineResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UpdateMachineResponse is the response of the UpdateMachine hook. The status of the update operation is determined by the CommonRetryResponse fields: - Status=Success + RetryAfterSeconds > 0: update is in progress - Status=Success + RetryAfterSeconds = 0: update completed successfully - Status=Failure: update failed", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status of the call. One of \"Success\" or \"Failure\".\n\nPossible enum values:\n - `\"Failure\"` represents a failure response.\n - `\"Success\"` represents a success response.", + Default: "", + Type: []string{"string"}, + Format: "", + Enum: []interface{}{"Failure", "Success"}, + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "message is a human-readable description of the status of the call.", + Type: []string{"string"}, + Format: "", + }, + }, + "retryAfterSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "retryAfterSeconds when set to a non-zero value signifies that the hook will be called again at a future time.", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"status", "retryAfterSeconds"}, + }, + }, + } +} + +func schema_api_runtime_hooks_v1alpha1_UpgradeStep(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UpgradeStep represents a single version upgrade step.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "version": { + SchemaProps: spec.SchemaProps{ + Description: "version is the Kubernetes version for this upgrade step.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"version"}, + }, + }, + } +} + +func schema_api_runtime_hooks_v1alpha1_UpgradeStepInfo(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UpgradeStepInfo provide info about a single version upgrade step.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "version": { + SchemaProps: spec.SchemaProps{ + Description: "version is the Kubernetes version for this upgrade step.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"version"}, + }, + }, + } +} + func schema_api_runtime_hooks_v1alpha1_ValidateTopologyRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/cloudbuild-nightly.yaml b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/cloudbuild-nightly.yaml index 8b25762454..07aaee686b 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/cloudbuild-nightly.yaml +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/cloudbuild-nightly.yaml @@ -5,7 +5,7 @@ options: substitution_option: ALLOW_LOOSE machineType: 'E2_HIGHCPU_8' steps: - - name: 'gcr.io/k8s-staging-test-infra/gcb-docker-gcloud@sha256:63840f133e0dfeea0af9ef391210da7fab9d2676172e2967fccab0cd6110c4e7' # v20250513-9264efb079 + - name: 'gcr.io/k8s-staging-test-infra/gcb-docker-gcloud@sha256:8d6a3a5b895e6776dbe9115b75db1412fbe57299b8db329d45cb54680e462b0b' # v20251211-4c812d4cd8 entrypoint: make env: - DOCKER_CLI_EXPERIMENTAL=enabled diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/cloudbuild.yaml b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/cloudbuild.yaml index 57d597590c..097cc2181b 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/cloudbuild.yaml +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/cloudbuild.yaml @@ -5,7 +5,7 @@ options: substitution_option: ALLOW_LOOSE machineType: 'E2_HIGHCPU_8' steps: - - name: 'gcr.io/k8s-staging-test-infra/gcb-docker-gcloud@sha256:63840f133e0dfeea0af9ef391210da7fab9d2676172e2967fccab0cd6110c4e7' # v20250513-9264efb079 + - name: 'gcr.io/k8s-staging-test-infra/gcb-docker-gcloud@sha256:8d6a3a5b895e6776dbe9115b75db1412fbe57299b8db329d45cb54680e462b0b' # v20251211-4c812d4cd8 entrypoint: make env: - DOCKER_CLI_EXPERIMENTAL=enabled diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/alias.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/alias.go index 63895489b1..b8fc4d21e1 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/alias.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/alias.go @@ -34,9 +34,11 @@ import ( clusterclasscontroller "sigs.k8s.io/cluster-api/internal/controllers/clusterclass" "sigs.k8s.io/cluster-api/internal/controllers/clusterresourceset" "sigs.k8s.io/cluster-api/internal/controllers/clusterresourcesetbinding" + extensionconfigcontroller "sigs.k8s.io/cluster-api/internal/controllers/extensionconfig" machinecontroller "sigs.k8s.io/cluster-api/internal/controllers/machine" machinedeploymentcontroller "sigs.k8s.io/cluster-api/internal/controllers/machinedeployment" machinehealthcheckcontroller "sigs.k8s.io/cluster-api/internal/controllers/machinehealthcheck" + machinepoolcontroller "sigs.k8s.io/cluster-api/internal/controllers/machinepool" machinesetcontroller "sigs.k8s.io/cluster-api/internal/controllers/machineset" clustertopologycontroller "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster" machinedeploymenttopologycontroller "sigs.k8s.io/cluster-api/internal/controllers/topology/machinedeployment" @@ -70,9 +72,10 @@ func (r *ClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manag // MachineReconciler reconciles a Machine object. type MachineReconciler struct { - Client client.Client - APIReader client.Reader - ClusterCache clustercache.ClusterCache + Client client.Client + APIReader client.Reader + ClusterCache clustercache.ClusterCache + RuntimeClient runtimeclient.Client // WatchFilterValue is the label value used to filter events prior to reconciliation. WatchFilterValue string @@ -88,6 +91,7 @@ func (r *MachineReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manag Client: r.Client, APIReader: r.APIReader, ClusterCache: r.ClusterCache, + RuntimeClient: r.RuntimeClient, WatchFilterValue: r.WatchFilterValue, RemoteConditionsGracePeriod: r.RemoteConditionsGracePeriod, AdditionalSyncMachineLabels: r.AdditionalSyncMachineLabels, @@ -119,8 +123,9 @@ func (r *MachineSetReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Ma // MachineDeploymentReconciler reconciles a MachineDeployment object. type MachineDeploymentReconciler struct { - Client client.Client - APIReader client.Reader + Client client.Client + APIReader client.Reader + RuntimeClient runtimeclient.Client // WatchFilterValue is the label value used to filter events prior to reconciliation. WatchFilterValue string @@ -130,6 +135,7 @@ func (r *MachineDeploymentReconciler) SetupWithManager(ctx context.Context, mgr return (&machinedeploymentcontroller.Reconciler{ Client: r.Client, APIReader: r.APIReader, + RuntimeClient: r.RuntimeClient, WatchFilterValue: r.WatchFilterValue, }).SetupWithManager(ctx, mgr, options) } @@ -279,3 +285,45 @@ func (r *ClusterResourceSetBindingReconciler) SetupWithManager(ctx context.Conte WatchFilterValue: r.WatchFilterValue, }).SetupWithManager(ctx, mgr, options) } + +// MachinePoolReconciler reconciles a MachinePool object. +type MachinePoolReconciler struct { + Client client.Client + APIReader client.Reader + ClusterCache clustercache.ClusterCache + + // WatchFilterValue is the label value used to filter events prior to reconciliation. + WatchFilterValue string +} + +func (r *MachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { + return (&machinepoolcontroller.Reconciler{ + Client: r.Client, + APIReader: r.APIReader, + ClusterCache: r.ClusterCache, + WatchFilterValue: r.WatchFilterValue, + }).SetupWithManager(ctx, mgr, options) +} + +// ExtensionConfigReconciler reconciles an ExtensionConfig object. +type ExtensionConfigReconciler struct { + Client client.Client + APIReader client.Reader + RuntimeClient runtimeclient.Client + PartialSecretCache cache.Cache + ReadOnly bool + + // WatchFilterValue is the label value used to filter events prior to reconciliation. + WatchFilterValue string +} + +func (r *ExtensionConfigReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { + return (&extensionconfigcontroller.Reconciler{ + Client: r.Client, + APIReader: r.APIReader, + RuntimeClient: r.RuntimeClient, + PartialSecretCache: r.PartialSecretCache, + ReadOnly: r.ReadOnly, + WatchFilterValue: r.WatchFilterValue, + }).SetupWithManager(ctx, mgr, options) +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_accessor.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_accessor.go index 0bbc92f238..d981607cd6 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_accessor.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_accessor.go @@ -82,6 +82,10 @@ type clusterAccessorConfig struct { // connection after creating a connection failed. ConnectionCreationRetryInterval time.Duration + // DisableClientCertificatePrivateKey is the flag to disable the creation of the client + // certificate private key. + DisableClientCertificatePrivateKey bool + // Cache is the config used for the cache that the clusterAccessor creates. Cache *clusterAccessorCacheConfig @@ -191,6 +195,10 @@ type clusterAccessorLockedConnectionState struct { // all typed objects except the ones for which caching has been disabled via DisableFor. cachedClient client.Client + // uncachedClient to communicate with the workload cluster. + // It performs live GET/LIST calls directly against the API server with no caching. + uncachedClient client.Client + // cache is the cache used by the client. // It manages informers that have been created e.g. by adding indexes to the cache, // Get & List calls from the client or via the Watch method of the clusterAccessor. @@ -280,7 +288,7 @@ func (ca *clusterAccessor) Connect(ctx context.Context) (retErr error) { // Only generate the clientCertificatePrivateKey once as there is no need to regenerate it after disconnect/connect. // Note: This has to be done before setting connection, because otherwise this code wouldn't be re-entrant if the // private key generation fails because we check Connected above. - if ca.lockedState.clientCertificatePrivateKey == nil { + if ca.lockedState.clientCertificatePrivateKey == nil && !ca.config.DisableClientCertificatePrivateKey { log.V(6).Info("Generating client certificate private key") clientCertificatePrivateKey, err := certs.NewPrivateKey() if err != nil { @@ -297,11 +305,12 @@ func (ca *clusterAccessor) Connect(ctx context.Context) (retErr error) { consecutiveFailures: 0, } ca.lockedState.connection = &clusterAccessorLockedConnectionState{ - restConfig: connection.RESTConfig, - restClient: connection.RESTClient, - cachedClient: connection.CachedClient, - cache: connection.Cache, - watches: sets.Set[string]{}, + restConfig: connection.RESTConfig, + restClient: connection.RESTClient, + cachedClient: connection.CachedClient, + uncachedClient: connection.UncachedClient, + cache: connection.Cache, + watches: sets.Set[string]{}, } return nil @@ -407,6 +416,18 @@ func (ca *clusterAccessor) GetReader(ctx context.Context) (client.Reader, error) return ca.lockedState.connection.cachedClient, nil } +// GetUncachedClient returns a live (uncached) client for the given cluster. +func (ca *clusterAccessor) GetUncachedClient(ctx context.Context) (client.Client, error) { + ca.rLock(ctx) + defer ca.rUnlock(ctx) + + if ca.lockedState.connection == nil { + return nil, errors.Wrapf(ErrClusterNotConnected, "error getting uncached client") + } + + return ca.lockedState.connection.uncachedClient, nil +} + func (ca *clusterAccessor) GetRESTConfig(ctx context.Context) (*rest.Config, error) { ca.rLock(ctx) defer ca.rUnlock(ctx) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_accessor_client.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_accessor_client.go index 7e5ae24846..8cc9a0a634 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_accessor_client.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_accessor_client.go @@ -42,10 +42,11 @@ import ( ) type createConnectionResult struct { - RESTConfig *rest.Config - RESTClient *rest.RESTClient - CachedClient client.Client - Cache *stoppableCache + RESTConfig *rest.Config + RESTClient *rest.RESTClient + CachedClient client.Client + UncachedClient client.Client + Cache *stoppableCache } func (ca *clusterAccessor) createConnection(ctx context.Context) (*createConnectionResult, error) { @@ -97,6 +98,12 @@ func (ca *clusterAccessor) createConnection(ctx context.Context) (*createConnect if err != nil { return nil, errors.Wrapf(err, "error creating HTTP client and mapper (using in-cluster config)") } + + log.V(6).Info(fmt.Sprintf("Creating uncached client with updated REST config with host %q", restConfig.Host)) + uncachedClient, err = createUncachedClient(ca.config.Scheme, restConfig, httpClient, mapper) + if err != nil { + return nil, errors.Wrapf(err, "error creating uncached client (using in-cluster config)") + } } log.V(6).Info("Creating cached client and cache") @@ -106,10 +113,11 @@ func (ca *clusterAccessor) createConnection(ctx context.Context) (*createConnect } return &createConnectionResult{ - RESTConfig: restConfig, - RESTClient: restClient, - CachedClient: cachedClient, - Cache: cache, + RESTConfig: restConfig, + RESTClient: restClient, + CachedClient: cachedClient, + UncachedClient: uncachedClient, + Cache: cache, }, nil } @@ -208,7 +216,7 @@ func createUncachedClient(scheme *runtime.Scheme, config *rest.Config, httpClien return nil, errors.Wrapf(err, "error creating uncached client") } - return uncachedClient, nil + return newClientWithTimeout(uncachedClient, config.Timeout), nil } // createCachedClient creates a cached client for the given cluster, based on the rest.Config. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_cache.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_cache.go index 06d64f3986..cf4d20ffaf 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_cache.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_cache.go @@ -44,6 +44,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/util/predicates" ) @@ -134,6 +135,10 @@ type ClusterCache interface { // If there is no connection to the workload cluster ErrClusterNotConnected will be returned. GetReader(ctx context.Context, cluster client.ObjectKey) (client.Reader, error) + // GetUncachedClient returns a live (uncached) client for the given cluster. + // If there is no connection to the workload cluster ErrClusterNotConnected will be returned. + GetUncachedClient(ctx context.Context, cluster client.ObjectKey) (client.Client, error) + // GetRESTConfig returns a REST config for the given cluster. // If there is no connection to the workload cluster ErrClusterNotConnected will be returned. GetRESTConfig(ctx context.Context, cluster client.ObjectKey) (*rest.Config, error) @@ -143,6 +148,9 @@ type ClusterCache interface { // cert to communicate with etcd. // This private key is stored and cached in the ClusterCache because it's expensive to generate a new // private key in every single Reconcile. + // + // Deprecated: This method is deprecated and will be removed in a future release as caching a rsa.PrivateKey + // is outside the scope of the ClusterCache. GetClientCertificatePrivateKey(ctx context.Context, cluster client.ObjectKey) (*rsa.PrivateKey, error) // Watch watches a workload cluster for events. @@ -321,7 +329,8 @@ func SetupWithManager(ctx context.Context, mgr manager.Manager, options Options, cacheCtxCancel: cacheCtxCancel, } - err := ctrl.NewControllerManagedBy(mgr). + predicateLog := ctrl.LoggerFrom(ctx).WithValues("controller", "clustercache") + err := capicontrollerutil.NewControllerManagedBy(mgr, predicateLog). Named("clustercache"). For(&clusterv1.Cluster{}). WithOptions(controllerOptions). @@ -392,6 +401,16 @@ func (cc *clusterCache) GetReader(ctx context.Context, cluster client.ObjectKey) return accessor.GetReader(ctx) } +// GetUncachedClient returns a live (uncached) client for the given cluster. +// If there is no connection to the workload cluster ErrClusterNotConnected will be returned. +func (cc *clusterCache) GetUncachedClient(ctx context.Context, cluster client.ObjectKey) (client.Client, error) { + accessor := cc.getClusterAccessor(cluster) + if accessor == nil { + return nil, errors.Wrapf(ErrClusterNotConnected, "error getting uncached client") + } + return accessor.GetUncachedClient(ctx) +} + func (cc *clusterCache) GetRESTConfig(ctx context.Context, cluster client.ObjectKey) (*rest.Config, error) { accessor := cc.getClusterAccessor(cluster) if accessor == nil { @@ -681,6 +700,12 @@ func (cc *clusterCache) SetConnectionCreationRetryInterval(interval time.Duratio cc.clusterAccessorConfig.ConnectionCreationRetryInterval = interval } +// DisablePrivateKeyGeneration can be used to disable the creation of cluster cert private key on clusteraccessor. +// This method should only be used for tests and is not part of the public ClusterCache interface. +func (cc *clusterCache) DisablePrivateKeyGeneration() { + cc.clusterAccessorConfig.DisableClientCertificatePrivateKey = true +} + // Shutdown can be used to shut down the ClusterCache in unit tests. // This method should only be used for tests because it hasn't been designed for production usage // in a manager (race conditions with manager shutdown etc.). diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_cache_fake.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_cache_fake.go index b6e44c4038..f4f44b3da0 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_cache_fake.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/clustercache/cluster_cache_fake.go @@ -32,8 +32,9 @@ func NewFakeClusterCache(workloadClient client.Client, clusterKey client.ObjectK testCacheTracker.clusterAccessors[clusterKey] = &clusterAccessor{ lockedState: clusterAccessorLockedState{ connection: &clusterAccessorLockedConnectionState{ - cachedClient: workloadClient, - watches: sets.Set[string]{}.Insert(watchObjects...), + cachedClient: workloadClient, + uncachedClient: workloadClient, + watches: sets.Set[string]{}.Insert(watchObjects...), }, healthChecking: clusterAccessorLockedHealthCheckingState{ lastProbeTime: time.Now(), @@ -44,3 +45,8 @@ func NewFakeClusterCache(workloadClient client.Client, clusterKey client.ObjectK } return testCacheTracker } + +// NewFakeEmptyClusterCache creates a new empty ClusterCache that can be used by unit tests. +func NewFakeEmptyClusterCache() ClusterCache { + return &clusterCache{} +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/crdmigrator/crd_migrator.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/crdmigrator/crd_migrator.go index 4fdf83e8da..e56110fd0b 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/crdmigrator/crd_migrator.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/controllers/crdmigrator/crd_migrator.go @@ -45,6 +45,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/util/cache" "sigs.k8s.io/cluster-api/util/contract" "sigs.k8s.io/cluster-api/util/predicates" @@ -121,7 +122,7 @@ func (r *CRDMigrator) SetupWithManager(ctx context.Context, mgr ctrl.Manager, co } predicateLog := ctrl.LoggerFrom(ctx).WithValues("controller", "crdmigrator") - err := ctrl.NewControllerManagedBy(mgr). + err := capicontrollerutil.NewControllerManagedBy(mgr, predicateLog). For(&apiextensionsv1.CustomResourceDefinition{}, // This controller uses a PartialObjectMetadata watch/informer to avoid an informer for CRDs // to reduce memory usage. @@ -412,7 +413,7 @@ func (r *CRDMigrator) reconcileStorageVersionMigration(ctx context.Context, crd if migrationConfig.UseStatusForStorageVersionMigration { err = r.Client.Status().Patch(ctx, u, client.Apply, client.FieldOwner("crdmigrator")) } else { - err = r.Client.Patch(ctx, u, client.Apply, client.FieldOwner("crdmigrator")) + err = r.Client.Apply(ctx, client.ApplyConfigurationFromUnstructured(u), client.FieldOwner("crdmigrator")) } // If we got a NotFound error, the object no longer exists so no need to update it. // If we got a Conflict error, another client wrote the object already so no need to update it. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/controllers/alias.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/controllers/alias.go deleted file mode 100644 index 920ef26da2..0000000000 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/controllers/alias.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2021 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 controllers - -import ( - "context" - - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - - "sigs.k8s.io/cluster-api/controllers/clustercache" - machinepool "sigs.k8s.io/cluster-api/exp/internal/controllers" -) - -// MachinePoolReconciler reconciles a MachinePool object. -type MachinePoolReconciler struct { - Client client.Client - APIReader client.Reader - ClusterCache clustercache.ClusterCache - - // WatchFilterValue is the label value used to filter events prior to reconciliation. - WatchFilterValue string -} - -func (r *MachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { - return (&machinepool.MachinePoolReconciler{ - Client: r.Client, - APIReader: r.APIReader, - ClusterCache: r.ClusterCache, - WatchFilterValue: r.WatchFilterValue, - }).SetupWithManager(ctx, mgr, options) -} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/internal/webhooks/doc.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/internal/webhooks/doc.go deleted file mode 100644 index fded86ca9a..0000000000 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/internal/webhooks/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2022 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 webhooks implements experimental webhooks. -package webhooks diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/webhooks/alias.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/webhooks/alias.go deleted file mode 100644 index 092347cf72..0000000000 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/webhooks/alias.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2022 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 webhooks - -import ( - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - "sigs.k8s.io/cluster-api/exp/ipam/internal/webhooks" -) - -// IPAddress implements a validating and defaulting webhook for IPAddress. -type IPAddress struct { - Client client.Reader -} - -// SetupWebhookWithManager sets up IPAddress webhooks. -func (webhook *IPAddress) SetupWebhookWithManager(mgr ctrl.Manager) error { - return (&webhooks.IPAddress{ - Client: webhook.Client, - }).SetupWebhookWithManager(mgr) -} - -// IPAddressClaim implements a validating and defaulting webhook for IPAddressClaim. -type IPAddressClaim struct { -} - -// SetupWebhookWithManager sets up IPAddressClaim webhooks. -func (webhook *IPAddressClaim) SetupWebhookWithManager(mgr ctrl.Manager) error { - return (&webhooks.IPAddressClaim{}).SetupWebhookWithManager(mgr) -} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/webhooks/doc.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/webhooks/doc.go deleted file mode 100644 index ff54398db2..0000000000 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/webhooks/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2022 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 webhooks contains external webhook implementations for some of our API types. -package webhooks diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/client/client.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/client/client.go index 3fb39f7634..714817f5f6 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/client/client.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/client/client.go @@ -20,7 +20,7 @@ package client import ( "context" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" runtimev1 "sigs.k8s.io/cluster-api/api/runtime/v1beta2" @@ -86,9 +86,12 @@ type Client interface { // Unregister unregisters the ExtensionConfig. Unregister(extensionConfig *runtimev1.ExtensionConfig) error + // GetAllExtensions gets all the ExtensionHandlers registered for the hook. + GetAllExtensions(ctx context.Context, hook runtimecatalog.Hook, forObject client.Object) ([]string, error) + // CallAllExtensions calls all the ExtensionHandler registered for the hook. - CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject) error + CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook, forObject client.Object, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject) error // CallExtension calls the ExtensionHandler with the given name. - CallExtension(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, name string, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject, opts ...CallExtensionOption) error + CallExtension(ctx context.Context, hook runtimecatalog.Hook, forObject client.Object, name string, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject, opts ...CallExtensionOption) error } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/controllers/alias.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/controllers/alias.go deleted file mode 100644 index 48b1a173a3..0000000000 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/controllers/alias.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright 2022 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 controllers - -import ( - "context" - - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/cache" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - - runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" - runtimecontrollers "sigs.k8s.io/cluster-api/exp/runtime/internal/controllers" -) - -// ExtensionConfigReconciler reconciles an ExtensionConfig object. -type ExtensionConfigReconciler struct { - Client client.Client - APIReader client.Reader - RuntimeClient runtimeclient.Client - - // WatchFilterValue is the label value used to filter events prior to reconciliation. - WatchFilterValue string -} - -func (r *ExtensionConfigReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options, partialSecretCache cache.Cache) error { - return (&runtimecontrollers.Reconciler{ - Client: r.Client, - APIReader: r.APIReader, - RuntimeClient: r.RuntimeClient, - WatchFilterValue: r.WatchFilterValue, - }).SetupWithManager(ctx, mgr, options, partialSecretCache) -} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/doc.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/doc.go deleted file mode 100644 index bc5c76004d..0000000000 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2022 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 controllers implements the exp/runtime controllers. -package controllers diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/desiredstate/desired_state.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/desiredstate/desired_state.go index c0f35f29b0..4b385d7c24 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/desiredstate/desired_state.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/desiredstate/desired_state.go @@ -22,8 +22,6 @@ import ( "fmt" "maps" "reflect" - "slices" - "strings" "time" "github.com/pkg/errors" @@ -54,6 +52,7 @@ import ( "sigs.k8s.io/cluster-api/internal/topology/selectors" "sigs.k8s.io/cluster-api/internal/webhooks" "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/cache" "sigs.k8s.io/cluster-api/util/conversion" ) @@ -63,13 +62,23 @@ type Generator interface { } // NewGenerator creates a new generator to generate desired state. -func NewGenerator(client client.Client, clusterCache clustercache.ClusterCache, runtimeClient runtimeclient.Client) Generator { - return &generator{ - Client: client, - ClusterCache: clusterCache, - RuntimeClient: runtimeClient, - patchEngine: patches.NewEngine(client, runtimeClient), +func NewGenerator(client client.Client, clusterCache clustercache.ClusterCache, runtimeClient runtimeclient.Client, hookCache cache.Cache[cache.HookEntry], getUpgradePlanCache cache.Cache[GenerateUpgradePlanCacheEntry]) (Generator, error) { + if client == nil || clusterCache == nil { + return nil, errors.New("Client and ClusterCache must not be nil") + } + + if feature.Gates.Enabled(feature.RuntimeSDK) && runtimeClient == nil { + return nil, errors.New("RuntimeClient must not be nil") } + + return &generator{ + Client: client, + ClusterCache: clusterCache, + RuntimeClient: runtimeClient, + hookCache: hookCache, + getUpgradePlanCache: getUpgradePlanCache, + patchEngine: patches.NewEngine(client, runtimeClient), + }, nil } // generator is a generator to generate desired state. @@ -81,6 +90,9 @@ type generator struct { RuntimeClient runtimeclient.Client + hookCache cache.Cache[cache.HookEntry] + getUpgradePlanCache cache.Cache[GenerateUpgradePlanCacheEntry] + // patchEngine is used to apply patches during computeDesiredState. patchEngine patches.Engine } @@ -107,6 +119,21 @@ func (g *generator) Generate(ctx context.Context, s *scope.Scope) (*scope.Cluste } } + // Compute the upgradePlan. + // By default CAPI allows to upgrade only by one minor, but if the cluster class defines an upgrade plan extension, + // the upgrade plan will be computed by calling the extension. Otherwise, if the cluster class defines a list of + // Kubernetes versions, the upgrade plan will be inferred from those versions. + // Runtime extension takes precedence if defined. + getUpgradePlan := GetUpgradePlanOneMinor + if s.Blueprint.ClusterClass.Spec.Upgrade.External.GenerateUpgradePlanExtension != "" { + getUpgradePlan = GetUpgradePlanFromExtension(g.RuntimeClient, g.getUpgradePlanCache, s.Current.Cluster, s.Blueprint.ClusterClass.Spec.Upgrade.External.GenerateUpgradePlanExtension) + } else if len(s.Blueprint.ClusterClass.Spec.KubernetesVersions) > 0 { + getUpgradePlan = GetUpgradePlanFromClusterClassVersions(s.Blueprint.ClusterClass.Spec.KubernetesVersions) + } + if err := ComputeUpgradePlan(ctx, s, getUpgradePlan); err != nil { + return nil, err + } + // Mark all the MachineDeployments that are currently upgrading. // This captured information is used for: // - Building the TopologyReconciled condition. @@ -170,7 +197,10 @@ func (g *generator) Generate(ctx context.Context, s *scope.Scope) (*scope.Cluste // Compute the desired state for the Cluster object adding a reference to the // InfrastructureCluster and the ControlPlane objects generated by the previous step. - desiredState.Cluster = computeCluster(ctx, s, desiredState.InfrastructureCluster, desiredState.ControlPlane.Object) + desiredState.Cluster, err = computeCluster(ctx, s, desiredState.InfrastructureCluster, desiredState.ControlPlane.Object) + if err != nil { + return nil, errors.Wrapf(err, "failed to compute Cluster") + } // If required, compute the desired state of the MachineDeployments from the list of MachineDeploymentTopologies // defined in the cluster. @@ -471,7 +501,7 @@ func (g *generator) computeControlPlane(ctx context.Context, s *scope.Scope, inf // Sets the desired Kubernetes version for the control plane. version, err := g.computeControlPlaneVersion(ctx, s) if err != nil { - return nil, errors.Wrap(err, "failed to compute version of control plane") + return nil, errors.Wrap(err, "failed to compute version of ControlPlane") } if err := contract.ControlPlane().Version().Set(controlPlane, version); err != nil { return nil, errors.Wrapf(err, "failed to set %s in the ControlPlane object", contract.ControlPlane().Version().Path()) @@ -485,10 +515,11 @@ func (g *generator) computeControlPlane(ctx context.Context, s *scope.Scope, inf // and the version defined in the topology. func (g *generator) computeControlPlaneVersion(ctx context.Context, s *scope.Scope) (string, error) { log := ctrl.LoggerFrom(ctx) - desiredVersion := s.Blueprint.Topology.Version + + topologyVersion := s.Blueprint.Topology.Version // If we are creating the control plane object (current control plane is nil), use version from topology. if s.Current.ControlPlane == nil || s.Current.ControlPlane.Object == nil { - return desiredVersion, nil + return topologyVersion, nil } // Get the current currentVersion of the control plane. @@ -497,11 +528,12 @@ func (g *generator) computeControlPlaneVersion(ctx context.Context, s *scope.Sco return "", errors.Wrap(err, "failed to get the version from control plane spec") } + // Track if the control plane needs an update. + // NOTE: in case the control plane don't need an update do not return immediately for: + // - computing a few more info for the update tracker, used to show the appropriate message for the TopologyReconciled condition. + // - call the AfterControlPlaneUpgrade hook (if not already called). s.UpgradeTracker.ControlPlane.IsPendingUpgrade = true - if *currentVersion == desiredVersion { - // Mark that the control plane spec is already at the desired version. - // This information is used to show the appropriate message for the TopologyReconciled - // condition. + if *currentVersion == topologyVersion { s.UpgradeTracker.ControlPlane.IsPendingUpgrade = false } @@ -510,8 +542,8 @@ func (g *generator) computeControlPlaneVersion(ctx context.Context, s *scope.Sco if err != nil { return "", errors.Wrap(err, "failed to check if the control plane is being provisioned") } - // If the control plane is being provisioned (being craeted for the first time), then do not - // pick up the desiredVersion yet. + // If the control plane is being provisioned (being created for the first time), then do not + // pick up the topologyVersion yet. // Return the current version of the control plane. We will pick up the new version after the // control plane is provisioned. if cpProvisioning { @@ -525,7 +557,7 @@ func (g *generator) computeControlPlaneVersion(ctx context.Context, s *scope.Sco return "", errors.Wrap(err, "failed to check if control plane is upgrading") } // If the current control plane is upgrading (still completing a previous upgrade), - // then do not pick up the desiredVersion yet. + // then do not pick up the topologyVersion yet. // Return the current version of the control plane. We will pick up the new version // after the control plane is stable. if cpUpgrading { @@ -533,136 +565,154 @@ func (g *generator) computeControlPlaneVersion(ctx context.Context, s *scope.Sco return *currentVersion, nil } - // Return here if the control plane is already at the desired version - if !s.UpgradeTracker.ControlPlane.IsPendingUpgrade { - // At this stage the control plane is not upgrading and is already at the desired version. - // We can return. - // Nb. We do not return early in the function if the control plane is already at the desired version so as - // to know if the control plane is being upgraded. This information - // is required when updating the TopologyReconciled condition on the cluster. - - // Call the AfterControlPlaneUpgrade now that the control plane is upgraded. - if feature.Gates.Enabled(feature.RuntimeSDK) { - // Call the hook only if we are tracking the intent to do so. If it is not tracked it means we don't need to call the - // hook because we didn't go through an upgrade or we already called the hook after the upgrade. - if hooks.IsPending(runtimehooksv1.AfterControlPlaneUpgrade, s.Current.Cluster) { - v1beta1Cluster := &clusterv1beta1.Cluster{} - // DeepCopy cluster because ConvertFrom has side effects like adding the conversion annotation. - if err := v1beta1Cluster.ConvertFrom(s.Current.Cluster.DeepCopy()); err != nil { - return "", errors.Wrap(err, "error converting Cluster to v1beta1 Cluster") - } + // if the control plane is not upgrading, before making further considerations about if to pick up another version, + // we should call the AfterControlPlaneUpgrade and the BeforeWorkersUpgrade hooks if not already done. + if feature.Gates.Enabled(feature.RuntimeSDK) { + // Note: calling the AfterControlPlaneUpgrade is the final step of a control plane upgrade. + hookCompleted, err := g.callAfterControlPlaneUpgradeHook(ctx, s, currentVersion) + if err != nil { + return "", err + } + if !hookCompleted { + return *currentVersion, nil + } - // Call all the registered extension for the hook. - hookRequest := &runtimehooksv1.AfterControlPlaneUpgradeRequest{ - Cluster: *cleanupCluster(v1beta1Cluster), - KubernetesVersion: desiredVersion, - } - hookResponse := &runtimehooksv1.AfterControlPlaneUpgradeResponse{} - if err := g.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.AfterControlPlaneUpgrade, s.Current.Cluster, hookRequest, hookResponse); err != nil { - return "", err - } - // Add the response to the tracker so we can later update condition or requeue when required. - s.HookResponseTracker.Add(runtimehooksv1.AfterControlPlaneUpgrade, hookResponse) - - // If the extension responds to hold off on starting Machine deployments upgrades, - // change the UpgradeTracker accordingly, otherwise the hook call is completed and we - // can remove this hook from the list of pending-hooks. - if hookResponse.RetryAfterSeconds != 0 { - log.Info(fmt.Sprintf("MachineDeployments/MachinePools upgrade to version %q are blocked by %q hook", desiredVersion, runtimecatalog.HookName(runtimehooksv1.AfterControlPlaneUpgrade))) - } else { - if err := hooks.MarkAsDone(ctx, g.Client, s.Current.Cluster, runtimehooksv1.AfterControlPlaneUpgrade); err != nil { - return "", err - } - } - } + // Note: calling the BeforeWorkersUpgrade is the first part of the execution of a worker upgrade step from the upgrade plan. + // The call to this hook is implemented in this function in order to ensure the hook is called + // after AfterControlPlaneUpgrade unblocks, and also to ensure that BeforeWorkersUpgrade + // can block the control plane upgrade to proceed in the upgrade plan. + // Note: this operation is a no-op if workers are not required to upgrade to the current control plane version. + hookCompleted, err = g.callBeforeWorkersUpgradeHook(ctx, s, &s.UpgradeTracker.MinWorkersVersion, *currentVersion) + if err != nil { + return "", err + } + if !hookCompleted { + return *currentVersion, nil } + } + // Before considering picking up the next control plane version, check if workers are required + // to upgrade first, e.g. to avoid breaking rules defining the max version skew between control plane + // and workers. + // If the MachineDeployments/MachinePools are required to upgrade, then do not pick up the next control plane version yet. + // We will pick up the new version after the MachineDeployments/MachinePools finish upgrading. + if len(s.UpgradeTracker.MachineDeployments.UpgradePlan) > 0 && s.UpgradeTracker.MachineDeployments.UpgradePlan[0] == *currentVersion { + s.UpgradeTracker.ControlPlane.IsWaitingForWorkersUpgrade = true + return *currentVersion, nil + } + if len(s.UpgradeTracker.MachinePools.UpgradePlan) > 0 && s.UpgradeTracker.MachinePools.UpgradePlan[0] == *currentVersion { + s.UpgradeTracker.ControlPlane.IsWaitingForWorkersUpgrade = true return *currentVersion, nil } - // If the control plane is not upgrading or scaling, we can assume the control plane is stable. - // However, we should also check for the MachineDeployments/MachinePools upgrading. - // If the MachineDeployments/MachinePools are upgrading, then do not pick up the desiredVersion yet. + // Also check if MachineDeployments/MachinePools are already upgrading. + // If the MachineDeployments/MachinePools are upgrading, then do not pick up the next control plane version yet. // We will pick up the new version after the MachineDeployments/MachinePools finish upgrading. - if len(s.UpgradeTracker.MachineDeployments.UpgradingNames()) > 0 || - len(s.UpgradeTracker.MachinePools.UpgradingNames()) > 0 { + if s.UpgradeTracker.MachineDeployments.IsAnyUpgrading() || s.UpgradeTracker.MachinePools.IsAnyUpgrading() { return *currentVersion, nil } + // At this point we can assume the control plane is stable and also MachineDeployments/MachinePools + // are not upgrading/are not required to upgrade. + + // If not already done, call the AfterWorkersUpgrade hook before picking up the desired version. + // (this is the last step of the previous upgrade). if feature.Gates.Enabled(feature.RuntimeSDK) { - var hookAnnotations []string - for key := range s.Current.Cluster.Annotations { - if strings.HasPrefix(key, clusterv1.BeforeClusterUpgradeHookAnnotationPrefix) { - hookAnnotations = append(hookAnnotations, key) - } + // Note: calling the AfterWorkersUpgrade is the last step of workers upgrade. + // The call to this hook is implemented in this function in order to ensure that AfterWorkersUpgrade + // can block the control plane upgrade to proceed in the upgrade plan. + // Note: this operation is a no-op if workers are not required to upgrade to the current control plane version. + hookCompleted, err := g.callAfterWorkersUpgradeHook(ctx, s, currentVersion) + if err != nil { + return "", err } - if len(hookAnnotations) > 0 { - slices.Sort(hookAnnotations) - message := fmt.Sprintf("annotations [%s] are set", strings.Join(hookAnnotations, ", ")) - if len(hookAnnotations) == 1 { - message = fmt.Sprintf("annotation [%s] is set", strings.Join(hookAnnotations, ", ")) - } - // Add the hook with a response to the tracker so we can later update the condition. - s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterUpgrade, &runtimehooksv1.BeforeClusterUpgradeResponse{ - CommonRetryResponse: runtimehooksv1.CommonRetryResponse{ - // RetryAfterSeconds needs to be set because having only hooks without RetryAfterSeconds - // would lead to not updating the condition. We can rely on getting an event when the - // annotation gets removed so we set twice of the default sync-period to not cause additional reconciles. - RetryAfterSeconds: 20 * 60, - CommonResponse: runtimehooksv1.CommonResponse{ - Message: message, - }, - }, - }) - - log.Info(fmt.Sprintf("Cluster upgrade to version %q is blocked by %q hook (via annotations)", desiredVersion, runtimecatalog.HookName(runtimehooksv1.BeforeClusterUpgrade)), "hooks", strings.Join(hookAnnotations, ",")) + if !hookCompleted { return *currentVersion, nil } + } + + // At this stage, we can assume the previous control plane upgrade is fully complete (including calling the AfterControlPlaneUpgrade). + // It is now possible to start making considerations if to pick up another version. - // At this point the control plane and the machine deployments are stable and we are almost ready to pick - // up the desiredVersion. Call the BeforeClusterUpgrade hook before picking up the desired version. - v1beta1Cluster := &clusterv1beta1.Cluster{} - // DeepCopy cluster because ConvertFrom has side effects like adding the conversion annotation. - if err := v1beta1Cluster.ConvertFrom(s.Current.Cluster.DeepCopy()); err != nil { - return "", errors.Wrap(err, "error converting Cluster to v1beta1 Cluster") + // If the control plane is not pending upgrade, then it is already at the desired version and there is no other version to pick up. + if !s.UpgradeTracker.ControlPlane.IsPendingUpgrade { + return *currentVersion, nil + } + + // If not already done, call the BeforeClusterUpgrade hook before picking up the desired version. + if feature.Gates.Enabled(feature.RuntimeSDK) { + // Note: calling the BeforeClusterUpgrade is the first step of an upgrade plan; + // this operation is a no-op for intermediate steps of an upgrade plan. + hookCompleted, err := g.callBeforeClusterUpgradeHook(ctx, s, currentVersion, topologyVersion) + if err != nil { + return "", err + } + if !hookCompleted { + return *currentVersion, nil } - hookRequest := &runtimehooksv1.BeforeClusterUpgradeRequest{ - Cluster: *cleanupCluster(v1beta1Cluster), - FromKubernetesVersion: *currentVersion, - ToKubernetesVersion: desiredVersion, + // After BeforeClusterUpgrade unblocked the upgrade, consider the upgrade started. + // As a consequence, the system start tracking the intent of calling AfterClusterUpgrade once the upgrade is complete. + // Note: this also prevent the BeforeClusterUpgrade to be called again (until after the upgrade is completed). + if err := hooks.MarkAsPending(ctx, g.Client, s.Current.Cluster, false, runtimehooksv1.AfterClusterUpgrade); err != nil { + return "", err } - hookResponse := &runtimehooksv1.BeforeClusterUpgradeResponse{} - if err := g.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.BeforeClusterUpgrade, s.Current.Cluster, hookRequest, hookResponse); err != nil { + } + + // Control plane and machine deployments are stable. The BeforeClusterUpgrade hook have been called. + // Ready to pick up the next version in the upgrade plan. + + // Select the next version for the control plane + if len(s.UpgradeTracker.ControlPlane.UpgradePlan) == 0 { + return "", errors.New("cannot compute the control plane version if the control plane is pending upgrade and the upgrade plan is not set") + } + nextVersion := s.UpgradeTracker.ControlPlane.UpgradePlan[0] + + if feature.Gates.Enabled(feature.RuntimeSDK) { + // Note: calling the BeforeControlPlaneUpgrade is the first step of a control plan upgrade step from the upgrade plan. + hookCompleted, err := g.callBeforeControlPlaneUpgradeHook(ctx, s, currentVersion, nextVersion) + if err != nil { return "", err } - // Add the response to the tracker so we can later update condition or requeue when required. - s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterUpgrade, hookResponse) - if hookResponse.RetryAfterSeconds != 0 { - // Cannot pickup the new version right now. Need to try again later. - log.Info(fmt.Sprintf("Cluster upgrade to version %q is blocked by %q hook", desiredVersion, runtimecatalog.HookName(runtimehooksv1.BeforeClusterUpgrade))) + if !hookCompleted { return *currentVersion, nil } - // We are picking up the new version here. - // Track the intent of calling the AfterControlPlaneUpgrade and the AfterClusterUpgrade hooks once we are done with the upgrade. - if err := hooks.MarkAsPending(ctx, g.Client, s.Current.Cluster, runtimehooksv1.AfterControlPlaneUpgrade, runtimehooksv1.AfterClusterUpgrade); err != nil { + // After BeforeControlPlaneUpgrade unblocked the upgrade step, consider the upgrade step start started, + // As a consequence, the system start tracking the intent of calling other hooks for this upgrade step: + // - AfterControlPlaneUpgrade hook to be called after the control plane completes the upgrade step. + // - If workers are required to upgrade to the current control plane version: + // - BeforeWorkersUpgrade hook to be called before workers start the upgrade step. + // - AfterWorkersUpgrade hook to be called after workers completes the upgrade step. + hooksToBeCalled := []runtimecatalog.Hook{runtimehooksv1.AfterControlPlaneUpgrade} + machineDeploymentPendingUpgrade := len(s.UpgradeTracker.MachineDeployments.UpgradePlan) > 0 && s.UpgradeTracker.MachineDeployments.UpgradePlan[0] == nextVersion + machinePoolPendingUpgrade := len(s.UpgradeTracker.MachinePools.UpgradePlan) > 0 && s.UpgradeTracker.MachinePools.UpgradePlan[0] == nextVersion + if machineDeploymentPendingUpgrade || machinePoolPendingUpgrade { + hooksToBeCalled = append(hooksToBeCalled, runtimehooksv1.BeforeWorkersUpgrade, runtimehooksv1.AfterWorkersUpgrade) + } + if err := hooks.MarkAsPending(ctx, g.Client, s.Current.Cluster, false, hooksToBeCalled...); err != nil { return "", err } } - // Control plane and machine deployments are stable. All the required hook are called. - // Ready to pick up the topology version. - s.UpgradeTracker.ControlPlane.IsPendingUpgrade = false + // The upgrade is now starting in this reconcile and not pending anymore. + // Note: it is important to unset IsPendingUpgrade, otherwise reconcileState will assume that we are still waiting for another upgrade (and thus defer the one we are starting). s.UpgradeTracker.ControlPlane.IsStartingUpgrade = true - return desiredVersion, nil + s.UpgradeTracker.ControlPlane.IsPendingUpgrade = false + + log.Info(fmt.Sprintf("Control plane %s upgraded from version %s to version %s", klog.KObj(s.Current.ControlPlane.Object), *currentVersion, nextVersion), + "ControlPlaneUpgrades", toUpgradeStep(s.UpgradeTracker.ControlPlane.UpgradePlan), + "WorkersUpgrades", toUpgradeStep(s.UpgradeTracker.MachineDeployments.UpgradePlan, s.UpgradeTracker.MachinePools.UpgradePlan), + s.Current.ControlPlane.Object.GetKind(), klog.KObj(s.Current.ControlPlane.Object), + ) + return nextVersion, nil } // computeCluster computes the desired state for the Cluster object. // NOTE: Some fields of the Cluster’s fields contribute to defining the Cluster blueprint (e.g. Cluster.Spec.Topology), // while some other fields should be managed as part of the actual Cluster (e.g. Cluster.Spec.ControlPlaneRef); in this func // we are concerned only about the latest group of fields. -func computeCluster(_ context.Context, s *scope.Scope, infrastructureCluster, controlPlane *unstructured.Unstructured) *clusterv1.Cluster { +func computeCluster(_ context.Context, s *scope.Scope, infrastructureCluster, controlPlane *unstructured.Unstructured) (*clusterv1.Cluster, error) { cluster := s.Current.Cluster.DeepCopy() // Enforce the topology labels. @@ -679,7 +729,29 @@ func computeCluster(_ context.Context, s *scope.Scope, infrastructureCluster, co cluster.Spec.InfrastructureRef = contract.ObjToContractVersionedObjectReference(infrastructureCluster) cluster.Spec.ControlPlaneRef = contract.ObjToContractVersionedObjectReference(controlPlane) - return cluster + // Track the current upgrade step in the cluster object (otherwise make sure we cleanup tracking of previous upgrades). + // NOTE: to detect if we are upgrading, we check if the intent to call the AfterClusterUpgrade is already tracked. + // NOTE, it is required to surface intermediate steps of the upgrade plan to allow creation of machines in KCP/MS. + // TODO: consider if we want to surface the upgrade plan (or the list of desired versions) in cluster status; + // TBD if the semantic of the new field can replace this annotation. + if cluster.Annotations == nil { + cluster.Annotations = map[string]string{} + } + if hooks.IsPending(runtimehooksv1.AfterClusterUpgrade, s.Current.Cluster) { + // NOTE: to detect if we are at the beginning of an upgrade, we check if the intent to call the AfterClusterUpgrade is already tracked. + controlPlaneVersion, err := contract.ControlPlane().Version().Get(controlPlane) + if err != nil { + return nil, errors.Wrap(err, "error getting control plane version") + } + cluster.Annotations[clusterv1.ClusterTopologyUpgradeStepAnnotation] = *controlPlaneVersion + } else { + // Note: Setting the annotation to "" instead of deleting it because we cannot be sure + // that we are able to remove the annotation from the Cluster with SSA if we lost ownership of + // the annotation in managedFields e.g. because of: https://github.com/kubernetes/kubernetes/issues/136919. + cluster.Annotations[clusterv1.ClusterTopologyUpgradeStepAnnotation] = "" + } + + return cluster, nil } // calculateRefDesiredAPIVersion returns the desired ref calculated from desiredReferencedObject @@ -804,7 +876,10 @@ func (g *generator) computeMachineDeployment(ctx context.Context, s *scope.Scope // Add ClusterTopologyMachineDeploymentLabel to the generated InfrastructureMachine template infraMachineTemplateLabels[clusterv1.ClusterTopologyMachineDeploymentNameLabel] = machineDeploymentTopology.Name desiredMachineDeployment.InfrastructureMachineTemplate.SetLabels(infraMachineTemplateLabels) - version := g.computeMachineDeploymentVersion(s, machineDeploymentTopology, currentMachineDeployment) + version, err := g.computeMachineDeploymentVersion(ctx, s, machineDeploymentTopology, currentMachineDeployment) + if err != nil { + return nil, err + } // Compute values that can be set both in the MachineDeploymentClass and in the MachineDeploymentTopology minReadySeconds := machineDeploymentClass.MinReadySeconds @@ -983,59 +1058,87 @@ func (g *generator) computeMachineDeployment(ctx context.Context, s *scope.Scope // computeMachineDeploymentVersion calculates the version of the desired machine deployment. // The version is calculated using the state of the current machine deployments, // the current control plane and the version defined in the topology. -func (g *generator) computeMachineDeploymentVersion(s *scope.Scope, machineDeploymentTopology clusterv1.MachineDeploymentTopology, currentMDState *scope.MachineDeploymentState) string { - desiredVersion := s.Blueprint.Topology.Version +func (g *generator) computeMachineDeploymentVersion(ctx context.Context, s *scope.Scope, machineDeploymentTopology clusterv1.MachineDeploymentTopology, currentMDState *scope.MachineDeploymentState) (string, error) { + log := ctrl.LoggerFrom(ctx) + + topologyVersion := s.Blueprint.Topology.Version // If creating a new machine deployment, mark it as pending if the control plane is not // yet stable. Creating a new MD while the control plane is upgrading can lead to unexpected race conditions. // Example: join could fail if the load balancers are slow in detecting when CP machines are // being deleted. if currentMDState == nil || currentMDState.Object == nil { - if !s.UpgradeTracker.ControlPlane.IsControlPlaneStable() || s.HookResponseTracker.IsBlocking(runtimehooksv1.AfterControlPlaneUpgrade) { + if !s.UpgradeTracker.ControlPlane.IsControlPlaneStable() || s.HookResponseTracker.IsBlocking(runtimehooksv1.AfterControlPlaneUpgrade) || s.HookResponseTracker.IsBlocking(runtimehooksv1.BeforeWorkersUpgrade) { s.UpgradeTracker.MachineDeployments.MarkPendingCreate(machineDeploymentTopology.Name) } - return desiredVersion + return topologyVersion, nil } // Get the current version of the machine deployment. currentVersion := currentMDState.Object.Spec.Template.Spec.Version - // Return early if the currentVersion is already equal to the desiredVersion + // Return early if the currentVersion is already equal to the topologyVersion // no further checks required. - if currentVersion == desiredVersion { - return currentVersion + if currentVersion == topologyVersion { + return currentVersion, nil } // Return early if the upgrade for the MachineDeployment is deferred. if isMachineDeploymentDeferred(s.Blueprint.Topology, machineDeploymentTopology) { s.UpgradeTracker.MachineDeployments.MarkDeferredUpgrade(currentMDState.Object.Name) s.UpgradeTracker.MachineDeployments.MarkPendingUpgrade(currentMDState.Object.Name) - return currentVersion + return currentVersion, nil } // Return early if the AfterControlPlaneUpgrade hook returns a blocking response. if s.HookResponseTracker.IsBlocking(runtimehooksv1.AfterControlPlaneUpgrade) { s.UpgradeTracker.MachineDeployments.MarkPendingUpgrade(currentMDState.Object.Name) - return currentVersion + return currentVersion, nil + } + + // Return early if the BeforeWorkersUpgrade hook returns a blocking response. + if s.HookResponseTracker.IsBlocking(runtimehooksv1.BeforeWorkersUpgrade) { + s.UpgradeTracker.MachineDeployments.MarkPendingUpgrade(currentMDState.Object.Name) + return currentVersion, nil } // Return early if the upgrade concurrency is reached. if s.UpgradeTracker.MachineDeployments.UpgradeConcurrencyReached() { s.UpgradeTracker.MachineDeployments.MarkPendingUpgrade(currentMDState.Object.Name) - return currentVersion + return currentVersion, nil } - // Return early if the Control Plane is not stable. Do not pick up the desiredVersion yet. + // Return early if the Control Plane is not stable. Do not pick up the topologyVersion yet. // Return the current version of the machine deployment. We will pick up the new version after the control // plane is stable. - if !s.UpgradeTracker.ControlPlane.IsControlPlaneStable() { + if !s.UpgradeTracker.ControlPlane.IsControlPlaneStableOrWaitingForWorkersUpgrade() { s.UpgradeTracker.MachineDeployments.MarkPendingUpgrade(currentMDState.Object.Name) - return currentVersion + return currentVersion, nil } // Control plane and machine deployments are stable. - // Ready to pick up the topology version. + // Ready to pick up the next version in the upgrade plan. + if len(s.UpgradeTracker.MachineDeployments.UpgradePlan) == 0 { + return "", errors.New("cannot compute the machine deployment version if the machine deployment is pending upgrade and the upgrade plan is not set") + } + + // The upgrade plan for workers has all versions from minWorkersVersion version to topologyVersion. + // If this MachineDeployment is already at minWorkersVersion, it should wait for the control plane to pick up next version before upgrading. + // Note: at this point we know that MachineDeployment is not yet at topologyVersion, so also set that MachineDeployment as PendingUpgrade. + if s.UpgradeTracker.MachineDeployments.UpgradePlan[0] == currentVersion { + s.UpgradeTracker.MachineDeployments.MarkPendingUpgrade(currentMDState.Object.Name) + return currentVersion, nil + } + s.UpgradeTracker.MachineDeployments.MarkUpgrading(currentMDState.Object.Name) - return desiredVersion + + nextVersion := s.UpgradeTracker.MachineDeployments.UpgradePlan[0] + + log.Info(fmt.Sprintf("MachineDeployment %s upgraded from version %s to version %s", klog.KObj(currentMDState.Object), currentVersion, nextVersion), + "ControlPlaneUpgrades", toUpgradeStep(s.UpgradeTracker.ControlPlane.UpgradePlan), + "WorkersUpgrades", toUpgradeStep(s.UpgradeTracker.MachineDeployments.UpgradePlan, s.UpgradeTracker.MachinePools.UpgradePlan), + "MachineDeployment", klog.KObj(currentMDState.Object), + ) + return nextVersion, nil } // isMachineDeploymentDeferred returns true if the upgrade for the mdTopology is deferred. @@ -1089,7 +1192,7 @@ func (g *generator) computeMachinePools(ctx context.Context, s *scope.Scope) (sc // computeMachinePool computes the desired state for a MachinePoolTopology. // The generated machinePool object is calculated using the values from the machinePoolTopology and // the machinePool class. -func (g *generator) computeMachinePool(_ context.Context, s *scope.Scope, machinePoolTopology clusterv1.MachinePoolTopology) (*scope.MachinePoolState, error) { +func (g *generator) computeMachinePool(ctx context.Context, s *scope.Scope, machinePoolTopology clusterv1.MachinePoolTopology) (*scope.MachinePoolState, error) { desiredMachinePool := &scope.MachinePoolState{} // Gets the blueprint for the MachinePool class. @@ -1167,7 +1270,10 @@ func (g *generator) computeMachinePool(_ context.Context, s *scope.Scope, machin // Add ClusterTopologyMachinePoolLabel to the generated InfrastructureMachinePool object infraMachinePoolObjectLabels[clusterv1.ClusterTopologyMachinePoolNameLabel] = machinePoolTopology.Name desiredMachinePool.InfrastructureMachinePoolObject.SetLabels(infraMachinePoolObjectLabels) - version := g.computeMachinePoolVersion(s, machinePoolTopology, currentMachinePool) + version, err := g.computeMachinePoolVersion(ctx, s, machinePoolTopology, currentMachinePool) + if err != nil { + return nil, err + } // Compute values that can be set both in the MachinePoolClass and in the MachinePoolTopology minReadySeconds := machinePoolClass.MinReadySeconds @@ -1280,59 +1386,87 @@ func (g *generator) computeMachinePool(_ context.Context, s *scope.Scope, machin // computeMachinePoolVersion calculates the version of the desired machine pool. // The version is calculated using the state of the current machine pools, // the current control plane and the version defined in the topology. -func (g *generator) computeMachinePoolVersion(s *scope.Scope, machinePoolTopology clusterv1.MachinePoolTopology, currentMPState *scope.MachinePoolState) string { - desiredVersion := s.Blueprint.Topology.Version +func (g *generator) computeMachinePoolVersion(ctx context.Context, s *scope.Scope, machinePoolTopology clusterv1.MachinePoolTopology, currentMPState *scope.MachinePoolState) (string, error) { + log := ctrl.LoggerFrom(ctx) + + topologyVersion := s.Blueprint.Topology.Version // If creating a new machine pool, mark it as pending if the control plane is not // yet stable. Creating a new MP while the control plane is upgrading can lead to unexpected race conditions. // Example: join could fail if the load balancers are slow in detecting when CP machines are // being deleted. if currentMPState == nil || currentMPState.Object == nil { - if !s.UpgradeTracker.ControlPlane.IsControlPlaneStable() || s.HookResponseTracker.IsBlocking(runtimehooksv1.AfterControlPlaneUpgrade) { + if !s.UpgradeTracker.ControlPlane.IsControlPlaneStable() || s.HookResponseTracker.IsBlocking(runtimehooksv1.AfterControlPlaneUpgrade) || s.HookResponseTracker.IsBlocking(runtimehooksv1.BeforeWorkersUpgrade) { s.UpgradeTracker.MachinePools.MarkPendingCreate(machinePoolTopology.Name) } - return desiredVersion + return topologyVersion, nil } // Get the current version of the machine pool. currentVersion := currentMPState.Object.Spec.Template.Spec.Version - // Return early if the currentVersion is already equal to the desiredVersion + // Return early if the currentVersion is already equal to the topologyVersion // no further checks required. - if currentVersion == desiredVersion { - return currentVersion + if currentVersion == topologyVersion { + return currentVersion, nil } // Return early if the upgrade for the MachinePool is deferred. if isMachinePoolDeferred(s.Blueprint.Topology, machinePoolTopology) { s.UpgradeTracker.MachinePools.MarkDeferredUpgrade(currentMPState.Object.Name) s.UpgradeTracker.MachinePools.MarkPendingUpgrade(currentMPState.Object.Name) - return currentVersion + return currentVersion, nil } // Return early if the AfterControlPlaneUpgrade hook returns a blocking response. if s.HookResponseTracker.IsBlocking(runtimehooksv1.AfterControlPlaneUpgrade) { s.UpgradeTracker.MachinePools.MarkPendingUpgrade(currentMPState.Object.Name) - return currentVersion + return currentVersion, nil + } + + // Return early if the BeforeWorkersUpgrade hook returns a blocking response. + if s.HookResponseTracker.IsBlocking(runtimehooksv1.BeforeWorkersUpgrade) { + s.UpgradeTracker.MachinePools.MarkPendingUpgrade(currentMPState.Object.Name) + return currentVersion, nil } // Return early if the upgrade concurrency is reached. if s.UpgradeTracker.MachinePools.UpgradeConcurrencyReached() { s.UpgradeTracker.MachinePools.MarkPendingUpgrade(currentMPState.Object.Name) - return currentVersion + return currentVersion, nil } - // Return early if the Control Plane is not stable. Do not pick up the desiredVersion yet. + // Return early if the Control Plane is not stable. Do not pick up the topologyVersion yet. // Return the current version of the machine pool. We will pick up the new version after the control // plane is stable. - if !s.UpgradeTracker.ControlPlane.IsControlPlaneStable() { + if !s.UpgradeTracker.ControlPlane.IsControlPlaneStableOrWaitingForWorkersUpgrade() { s.UpgradeTracker.MachinePools.MarkPendingUpgrade(currentMPState.Object.Name) - return currentVersion + return currentVersion, nil } // Control plane and machine pools are stable. // Ready to pick up the topology version. + if len(s.UpgradeTracker.MachinePools.UpgradePlan) == 0 { + return "", errors.New("cannot compute the machine pool version if the machine pool is pending upgrade and the upgrade plan is not set") + } + + // The upgrade plan for workers has all versions from minWorkersVersion version to topologyVersion. + // If this MachinePool is already at minWorkersVersion, it should wait for the control plane to pick up next version before upgrading. + // Note: at this point we know that MachinePool is not yet at topologyVersion, so also set that MachinePool is PendingUpgrade. + if s.UpgradeTracker.MachinePools.UpgradePlan[0] == currentVersion { + s.UpgradeTracker.MachinePools.MarkPendingUpgrade(currentMPState.Object.Name) + return currentVersion, nil + } + s.UpgradeTracker.MachinePools.MarkUpgrading(currentMPState.Object.Name) - return desiredVersion + + nextVersion := s.UpgradeTracker.MachinePools.UpgradePlan[0] + + log.Info(fmt.Sprintf("MachinePool %s upgraded from version %s to version %s", klog.KObj(currentMPState.Object), currentVersion, nextVersion), + "ControlPlaneUpgrades", toUpgradeStep(s.UpgradeTracker.ControlPlane.UpgradePlan), + "WorkersUpgrades", toUpgradeStep(s.UpgradeTracker.MachineDeployments.UpgradePlan, s.UpgradeTracker.MachinePools.UpgradePlan), + "MachinePool", klog.KObj(currentMPState.Object), + ) + return nextVersion, nil } // isMachinePoolDeferred returns true if the upgrade for the mpTopology is deferred. @@ -1536,7 +1670,7 @@ func getOwnerReferenceFrom(obj, owner client.Object) *metav1.OwnerReference { return nil } -func cleanupCluster(cluster *clusterv1beta1.Cluster) *clusterv1beta1.Cluster { +func cleanupV1Beta1Cluster(cluster *clusterv1beta1.Cluster) *clusterv1beta1.Cluster { // Optimize size of Cluster by not sending status, the managedFields and some specific annotations. cluster.SetManagedFields(nil) @@ -1551,3 +1685,19 @@ func cleanupCluster(cluster *clusterv1beta1.Cluster) *clusterv1beta1.Cluster { cluster.Status = clusterv1beta1.ClusterStatus{} return cluster } + +func cleanupCluster(cluster *clusterv1.Cluster) *clusterv1.Cluster { + // Optimize size of Cluster by not sending status, the managedFields and some specific annotations. + cluster.SetManagedFields(nil) + + // The conversion that we run before calling cleanupCluster does not clone annotations + // So we have to do it here to not modify the original Cluster. + if cluster.Annotations != nil { + annotations := maps.Clone(cluster.Annotations) + delete(annotations, corev1.LastAppliedConfigAnnotation) + delete(annotations, conversion.DataAnnotation) + cluster.Annotations = annotations + } + cluster.Status = clusterv1.ClusterStatus{} + return cluster +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/desiredstate/lifecycle_hooks.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/desiredstate/lifecycle_hooks.go new file mode 100644 index 0000000000..0581856cfe --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/desiredstate/lifecycle_hooks.go @@ -0,0 +1,420 @@ +/* +Copyright 2025 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 desiredstate + +import ( + "context" + "fmt" + "slices" + "strings" + "time" + + "github.com/pkg/errors" + ctrl "sigs.k8s.io/controller-runtime" + + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" + runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" + "sigs.k8s.io/cluster-api/exp/topology/scope" + "sigs.k8s.io/cluster-api/internal/hooks" + "sigs.k8s.io/cluster-api/util/cache" +) + +// callBeforeClusterUpgradeHook calls the BeforeClusterUpgrade at the beginning of an upgrade. +// NOTE: the hook should be called only at the beginning of an upgrade sequence (it should not be called when in the middle of a multistep upgrade sequence); +// to detect if we are at the beginning of an upgrade, the code checks if the intent to call the AfterClusterUpgrade is not yet tracked. +func (g *generator) callBeforeClusterUpgradeHook(ctx context.Context, s *scope.Scope, currentVersion *string, topologyVersion string) (bool, error) { + log := ctrl.LoggerFrom(ctx) + + if !hooks.IsPending(runtimehooksv1.AfterClusterUpgrade, s.Current.Cluster) { + var hookAnnotations []string + for key := range s.Current.Cluster.Annotations { + if strings.HasPrefix(key, clusterv1.BeforeClusterUpgradeHookAnnotationPrefix) { + hookAnnotations = append(hookAnnotations, key) + } + } + if len(hookAnnotations) > 0 { + slices.Sort(hookAnnotations) + message := fmt.Sprintf("annotations %s are set", strings.Join(hookAnnotations, ", ")) + if len(hookAnnotations) == 1 { + message = fmt.Sprintf("annotation %s is set", strings.Join(hookAnnotations, ", ")) + } + // Add the hook with a response to the tracker so we can later update the condition. + s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterUpgrade, &runtimehooksv1.BeforeClusterUpgradeResponse{ + CommonRetryResponse: runtimehooksv1.CommonRetryResponse{ + // RetryAfterSeconds needs to be set because having only hooks without RetryAfterSeconds + // would lead to not updating the condition. We can rely on getting an event when the + // annotation gets removed so we set twice of the default sync-period to not cause additional reconciles. + RetryAfterSeconds: 20 * 60, + CommonResponse: runtimehooksv1.CommonResponse{ + Message: message, + }, + }, + }) + + log.Info(fmt.Sprintf("Cluster upgrade from version %s to version %s is blocked by %s hook (via annotations)", *currentVersion, topologyVersion, runtimecatalog.HookName(runtimehooksv1.BeforeClusterUpgrade)), "hooks", strings.Join(hookAnnotations, ","), + "ControlPlaneUpgrades", toUpgradeStep(s.UpgradeTracker.ControlPlane.UpgradePlan), + "WorkersUpgrades", toUpgradeStep(s.UpgradeTracker.MachineDeployments.UpgradePlan, s.UpgradeTracker.MachinePools.UpgradePlan), + ) + return false, nil + } + + // Return quickly if the hook is not defined. + extensionHandlers, err := g.RuntimeClient.GetAllExtensions(ctx, runtimehooksv1.BeforeClusterUpgrade, s.Current.Cluster) + if err != nil { + return false, err + } + if len(extensionHandlers) == 0 { + return true, nil + } + + if cacheEntry, ok := g.hookCache.Has(cache.NewHookEntryKey(s.Current.Cluster, runtimehooksv1.BeforeClusterUpgrade)); ok { + if requeueAfter, requeue := cacheEntry.ShouldRequeue(time.Now()); requeue { + log.V(5).Info(fmt.Sprintf("Skip calling BeforeClusterUpgrade hook, retry after %s", requeueAfter)) + s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterUpgrade, cacheEntry.ToResponse(&runtimehooksv1.BeforeClusterUpgradeResponse{}, requeueAfter)) + return false, nil + } + } + + v1beta1Cluster := &clusterv1beta1.Cluster{} + // DeepCopy cluster because ConvertFrom has side effects like adding the conversion annotation. + if err := v1beta1Cluster.ConvertFrom(s.Current.Cluster.DeepCopy()); err != nil { + return false, errors.Wrap(err, "error converting Cluster to v1beta1 Cluster") + } + + hookRequest := &runtimehooksv1.BeforeClusterUpgradeRequest{ + Cluster: *cleanupV1Beta1Cluster(v1beta1Cluster), + FromKubernetesVersion: *currentVersion, + ToKubernetesVersion: topologyVersion, + ControlPlaneUpgrades: toUpgradeStep(s.UpgradeTracker.ControlPlane.UpgradePlan), + WorkersUpgrades: toUpgradeStep(s.UpgradeTracker.MachineDeployments.UpgradePlan, s.UpgradeTracker.MachinePools.UpgradePlan), + } + hookResponse := &runtimehooksv1.BeforeClusterUpgradeResponse{} + if err := g.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.BeforeClusterUpgrade, s.Current.Cluster, hookRequest, hookResponse); err != nil { + return false, err + } + // Add the response to the tracker so we can later update condition or requeue when required. + s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterUpgrade, hookResponse) + + if hookResponse.RetryAfterSeconds != 0 { + // Cannot pickup the new version right now. Need to try again later. + g.hookCache.Add(cache.NewHookEntry(s.Current.Cluster, runtimehooksv1.BeforeClusterUpgrade, time.Now().Add(time.Duration(hookResponse.RetryAfterSeconds)*time.Second), hookResponse.GetMessage())) + log.Info(fmt.Sprintf("Cluster upgrade from version %s to version %s is blocked by %s hook, retry after %ds", hookRequest.FromKubernetesVersion, hookRequest.ToKubernetesVersion, runtimecatalog.HookName(runtimehooksv1.BeforeClusterUpgrade), hookResponse.RetryAfterSeconds), + "ControlPlaneUpgrades", hookRequest.ControlPlaneUpgrades, + "WorkersUpgrades", hookRequest.WorkersUpgrades, + ) + return false, nil + } + + log.Info(fmt.Sprintf("Cluster upgrade from version %s to version %s unblocked by %s hook", hookRequest.FromKubernetesVersion, hookRequest.ToKubernetesVersion, runtimecatalog.HookName(runtimehooksv1.BeforeClusterUpgrade)), + "ControlPlaneUpgrades", hookRequest.ControlPlaneUpgrades, + "WorkersUpgrades", hookRequest.WorkersUpgrades, + ) + } + return true, nil +} + +// callBeforeControlPlaneUpgradeHook calls the BeforeControlPlaneUpgrade before the control plane picks up a new control plane version, +// no matter if this is an intermediate versions of an upgrade plan or the target version of an upgrade plan. +// NOTE: when an upgrade starts, the hook should be called after the BeforeClusterUpgrade hook. +// NOTE: the hook doesn't need call intent tracking: it is always called before picking up a new control plane version. +func (g *generator) callBeforeControlPlaneUpgradeHook(ctx context.Context, s *scope.Scope, currentVersion *string, nextVersion string) (bool, error) { + log := ctrl.LoggerFrom(ctx) + + // Return quickly if the hook is not defined. + extensionHandlers, err := g.RuntimeClient.GetAllExtensions(ctx, runtimehooksv1.BeforeControlPlaneUpgrade, s.Current.Cluster) + if err != nil { + return false, err + } + if len(extensionHandlers) == 0 { + return true, nil + } + + if cacheEntry, ok := g.hookCache.Has(cache.NewHookEntryKey(s.Current.Cluster, runtimehooksv1.BeforeControlPlaneUpgrade)); ok { + if requeueAfter, requeue := cacheEntry.ShouldRequeue(time.Now()); requeue { + log.V(5).Info(fmt.Sprintf("Skip calling BeforeControlPlaneUpgrade hook, retry after %s", requeueAfter)) + s.HookResponseTracker.Add(runtimehooksv1.BeforeControlPlaneUpgrade, cacheEntry.ToResponse(&runtimehooksv1.BeforeControlPlaneUpgradeResponse{}, requeueAfter)) + return false, nil + } + } + + // NOTE: the hook should always be called before piking up a new version. + v1beta1Cluster := &clusterv1beta1.Cluster{} + // DeepCopy cluster because ConvertFrom has side effects like adding the conversion annotation. + if err := v1beta1Cluster.ConvertFrom(s.Current.Cluster.DeepCopy()); err != nil { + return false, errors.Wrap(err, "error converting Cluster to v1beta1 Cluster") + } + + hookRequest := &runtimehooksv1.BeforeControlPlaneUpgradeRequest{ + Cluster: *cleanupV1Beta1Cluster(v1beta1Cluster), + FromKubernetesVersion: *currentVersion, + ToKubernetesVersion: nextVersion, + ControlPlaneUpgrades: toUpgradeStep(s.UpgradeTracker.ControlPlane.UpgradePlan), + WorkersUpgrades: toUpgradeStep(s.UpgradeTracker.MachineDeployments.UpgradePlan, s.UpgradeTracker.MachinePools.UpgradePlan), + } + hookResponse := &runtimehooksv1.BeforeControlPlaneUpgradeResponse{} + if err := g.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.BeforeControlPlaneUpgrade, s.Current.Cluster, hookRequest, hookResponse); err != nil { + return false, err + } + // Add the response to the tracker so we can later update condition or requeue when required. + s.HookResponseTracker.Add(runtimehooksv1.BeforeControlPlaneUpgrade, hookResponse) + + if hookResponse.RetryAfterSeconds != 0 { + // Cannot pickup the new version right now. Need to try again later. + g.hookCache.Add(cache.NewHookEntry(s.Current.Cluster, runtimehooksv1.BeforeControlPlaneUpgrade, time.Now().Add(time.Duration(hookResponse.RetryAfterSeconds)*time.Second), hookResponse.GetMessage())) + log.Info(fmt.Sprintf("Control plane upgrade from version %s to version %s is blocked by %s hook, retry after %ds", hookRequest.FromKubernetesVersion, hookRequest.ToKubernetesVersion, runtimecatalog.HookName(runtimehooksv1.BeforeControlPlaneUpgrade), hookResponse.RetryAfterSeconds), + "ControlPlaneUpgrades", hookRequest.ControlPlaneUpgrades, + "WorkersUpgrades", hookRequest.WorkersUpgrades, + ) + return false, nil + } + + log.Info(fmt.Sprintf("Control plane upgrade from version %s to version %s unblocked by %s hook", hookRequest.FromKubernetesVersion, hookRequest.ToKubernetesVersion, runtimecatalog.HookName(runtimehooksv1.BeforeControlPlaneUpgrade)), + "ControlPlaneUpgrades", hookRequest.ControlPlaneUpgrades, + "WorkersUpgrades", hookRequest.WorkersUpgrades, + ) + + return true, nil +} + +// callAfterControlPlaneUpgradeHook calls the AfterControlPlaneUpgrade after the control plane upgrade is completed, +// no matter if this is an intermediate versions of an upgrade plan or the target version of an upgrade plan. +// NOTE: computeControlPlaneVersion records intent to call this hook when picking up a new control plane version. +func (g *generator) callAfterControlPlaneUpgradeHook(ctx context.Context, s *scope.Scope, currentVersion *string) (bool, error) { + log := ctrl.LoggerFrom(ctx) + + // Call the hook only if we are tracking the intent to do so. If it is not tracked it means we don't need to call the + // hook because we didn't go through an upgrade or we already called the hook after the upgrade. + if hooks.IsPending(runtimehooksv1.AfterControlPlaneUpgrade, s.Current.Cluster) { + // Return quickly if the hook is not defined. + extensionHandlers, err := g.RuntimeClient.GetAllExtensions(ctx, runtimehooksv1.AfterControlPlaneUpgrade, s.Current.Cluster) + if err != nil { + return false, err + } + if len(extensionHandlers) == 0 { + if err := hooks.MarkAsDone(ctx, g.Client, s.Current.Cluster, false, runtimehooksv1.AfterControlPlaneUpgrade); err != nil { + return false, err + } + return true, nil + } + + if cacheEntry, ok := g.hookCache.Has(cache.NewHookEntryKey(s.Current.Cluster, runtimehooksv1.AfterControlPlaneUpgrade)); ok { + if requeueAfter, requeue := cacheEntry.ShouldRequeue(time.Now()); requeue { + log.V(5).Info(fmt.Sprintf("Skip calling AfterControlPlaneUpgrade hook, retry after %s", requeueAfter)) + s.HookResponseTracker.Add(runtimehooksv1.AfterControlPlaneUpgrade, cacheEntry.ToResponse(&runtimehooksv1.AfterControlPlaneUpgradeResponse{}, requeueAfter)) + return false, nil + } + } + + // DeepCopy cluster because ConvertFrom has side effects like adding the conversion annotation. + v1beta1Cluster := &clusterv1beta1.Cluster{} + if err := v1beta1Cluster.ConvertFrom(s.Current.Cluster.DeepCopy()); err != nil { + return false, errors.Wrap(err, "error converting Cluster to v1beta1 Cluster") + } + + // Call all the registered extension for the hook. + hookRequest := &runtimehooksv1.AfterControlPlaneUpgradeRequest{ + Cluster: *cleanupV1Beta1Cluster(v1beta1Cluster), + KubernetesVersion: *currentVersion, + ControlPlaneUpgrades: toUpgradeStep(s.UpgradeTracker.ControlPlane.UpgradePlan), + WorkersUpgrades: toUpgradeStep(s.UpgradeTracker.MachineDeployments.UpgradePlan, s.UpgradeTracker.MachinePools.UpgradePlan), + } + hookResponse := &runtimehooksv1.AfterControlPlaneUpgradeResponse{} + if err := g.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.AfterControlPlaneUpgrade, s.Current.Cluster, hookRequest, hookResponse); err != nil { + return false, err + } + // Add the response to the tracker so we can later update condition or requeue when required. + s.HookResponseTracker.Add(runtimehooksv1.AfterControlPlaneUpgrade, hookResponse) + + if hookResponse.RetryAfterSeconds != 0 { + g.hookCache.Add(cache.NewHookEntry(s.Current.Cluster, runtimehooksv1.AfterControlPlaneUpgrade, time.Now().Add(time.Duration(hookResponse.RetryAfterSeconds)*time.Second), hookResponse.GetMessage())) + log.Info(fmt.Sprintf("Control plane upgrade to version %s completed but next steps are blocked by %s hook, retry after %ds", hookRequest.KubernetesVersion, runtimecatalog.HookName(runtimehooksv1.AfterControlPlaneUpgrade), hookResponse.RetryAfterSeconds), + "ControlPlaneUpgrades", hookRequest.ControlPlaneUpgrades, + "WorkersUpgrades", hookRequest.WorkersUpgrades, + ) + return false, nil + } + if err := hooks.MarkAsDone(ctx, g.Client, s.Current.Cluster, false, runtimehooksv1.AfterControlPlaneUpgrade); err != nil { + return false, err + } + + log.Info(fmt.Sprintf("Control plane upgrade to version %s and %s hook completed, next steps unblocked", hookRequest.KubernetesVersion, runtimecatalog.HookName(runtimehooksv1.AfterControlPlaneUpgrade)), + "ControlPlaneUpgrades", hookRequest.ControlPlaneUpgrades, + "WorkersUpgrades", hookRequest.WorkersUpgrades, + ) + } + return true, nil +} + +// callBeforeWorkersUpgradeHook calls the BeforeWorkersUpgrade before workers starts picking up a new worker version, +// no matter if this is an intermediate versions of an upgrade plan or the target version of an upgrade plan. +// NOTE: computeControlPlaneVersion records intent to call this hook when picking up a new control plane version +// that exists also in the workers upgrade plan. +func (g *generator) callBeforeWorkersUpgradeHook(ctx context.Context, s *scope.Scope, currentVersion *string, nextVersion string) (bool, error) { + log := ctrl.LoggerFrom(ctx) + + // Call the hook only if we are tracking the intent to do so. If it is not tracked it means we don't need to call the + // hook because we didn't go through an upgrade or we already called the hook after the upgrade. + if hooks.IsPending(runtimehooksv1.BeforeWorkersUpgrade, s.Current.Cluster) { + // Return quickly if the hook is not defined. + extensionHandlers, err := g.RuntimeClient.GetAllExtensions(ctx, runtimehooksv1.BeforeWorkersUpgrade, s.Current.Cluster) + if err != nil { + return false, err + } + if len(extensionHandlers) == 0 { + if err := hooks.MarkAsDone(ctx, g.Client, s.Current.Cluster, false, runtimehooksv1.BeforeWorkersUpgrade); err != nil { + return false, err + } + return true, nil + } + + if cacheEntry, ok := g.hookCache.Has(cache.NewHookEntryKey(s.Current.Cluster, runtimehooksv1.BeforeWorkersUpgrade)); ok { + if requeueAfter, requeue := cacheEntry.ShouldRequeue(time.Now()); requeue { + log.V(5).Info(fmt.Sprintf("Skip calling BeforeWorkersUpgrade hook, retry after %s", requeueAfter)) + s.HookResponseTracker.Add(runtimehooksv1.BeforeWorkersUpgrade, cacheEntry.ToResponse(&runtimehooksv1.BeforeWorkersUpgradeResponse{}, requeueAfter)) + return false, nil + } + } + + // DeepCopy cluster because ConvertFrom has side effects like adding the conversion annotation. + v1beta1Cluster := &clusterv1beta1.Cluster{} + if err := v1beta1Cluster.ConvertFrom(s.Current.Cluster.DeepCopy()); err != nil { + return false, errors.Wrap(err, "error converting Cluster to v1beta1 Cluster") + } + + hookRequest := &runtimehooksv1.BeforeWorkersUpgradeRequest{ + Cluster: *cleanupV1Beta1Cluster(v1beta1Cluster), + FromKubernetesVersion: *currentVersion, + ToKubernetesVersion: nextVersion, + ControlPlaneUpgrades: toUpgradeStep(s.UpgradeTracker.ControlPlane.UpgradePlan), + WorkersUpgrades: toUpgradeStep(s.UpgradeTracker.MachineDeployments.UpgradePlan, s.UpgradeTracker.MachinePools.UpgradePlan), + } + hookResponse := &runtimehooksv1.BeforeWorkersUpgradeResponse{} + if err := g.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.BeforeWorkersUpgrade, s.Current.Cluster, hookRequest, hookResponse); err != nil { + return false, err + } + // Add the response to the tracker so we can later update condition or requeue when required. + s.HookResponseTracker.Add(runtimehooksv1.BeforeWorkersUpgrade, hookResponse) + + if hookResponse.RetryAfterSeconds != 0 { + // Cannot pickup the new version right now. Need to try again later. + g.hookCache.Add(cache.NewHookEntry(s.Current.Cluster, runtimehooksv1.BeforeWorkersUpgrade, time.Now().Add(time.Duration(hookResponse.RetryAfterSeconds)*time.Second), hookResponse.GetMessage())) + log.Info(fmt.Sprintf("Workers upgrade from version %s to version %s is blocked by %s hook, retry after %ds", hookRequest.FromKubernetesVersion, hookRequest.ToKubernetesVersion, runtimecatalog.HookName(runtimehooksv1.BeforeWorkersUpgrade), hookResponse.RetryAfterSeconds), + "ControlPlaneUpgrades", hookRequest.ControlPlaneUpgrades, + "WorkersUpgrades", hookRequest.WorkersUpgrades, + ) + return false, nil + } + if err := hooks.MarkAsDone(ctx, g.Client, s.Current.Cluster, false, runtimehooksv1.BeforeWorkersUpgrade); err != nil { + return false, err + } + + log.Info(fmt.Sprintf("Workers upgrade from version %s to version %s unblocked by %s hook", hookRequest.FromKubernetesVersion, hookRequest.ToKubernetesVersion, runtimecatalog.HookName(runtimehooksv1.BeforeWorkersUpgrade)), + "ControlPlaneUpgrades", hookRequest.ControlPlaneUpgrades, + "WorkersUpgrades", hookRequest.WorkersUpgrades, + ) + } + + return true, nil +} + +// callAfterWorkersUpgradeHook calls the AfterWorkersUpgrade after the worker upgrade is completed, +// no matter if this is an intermediate versions of an upgrade plan or the target version of an upgrade plan. +// NOTE: computeControlPlaneVersion records intent to call this hook when picking up a new control plane version +// that exists also in the workers upgrade plan. +func (g *generator) callAfterWorkersUpgradeHook(ctx context.Context, s *scope.Scope, currentVersion *string) (bool, error) { + log := ctrl.LoggerFrom(ctx) + + // Call the hook only if we are tracking the intent to do so. If it is not tracked it means we don't need to call the + // hook because we didn't go through an upgrade or we already called the hook after the upgrade. + if hooks.IsPending(runtimehooksv1.AfterWorkersUpgrade, s.Current.Cluster) { + // Return quickly if the hook is not defined. + extensionHandlers, err := g.RuntimeClient.GetAllExtensions(ctx, runtimehooksv1.AfterWorkersUpgrade, s.Current.Cluster) + if err != nil { + return false, err + } + if len(extensionHandlers) == 0 { + if err := hooks.MarkAsDone(ctx, g.Client, s.Current.Cluster, false, runtimehooksv1.AfterWorkersUpgrade); err != nil { + return false, err + } + return true, nil + } + + if cacheEntry, ok := g.hookCache.Has(cache.NewHookEntryKey(s.Current.Cluster, runtimehooksv1.AfterWorkersUpgrade)); ok { + if requeueAfter, requeue := cacheEntry.ShouldRequeue(time.Now()); requeue { + log.V(5).Info(fmt.Sprintf("Skip calling AfterWorkersUpgrade hook, retry after %s", requeueAfter)) + s.HookResponseTracker.Add(runtimehooksv1.AfterWorkersUpgrade, cacheEntry.ToResponse(&runtimehooksv1.AfterWorkersUpgradeResponse{}, requeueAfter)) + return false, nil + } + } + + // DeepCopy cluster because ConvertFrom has side effects like adding the conversion annotation. + v1beta1Cluster := &clusterv1beta1.Cluster{} + if err := v1beta1Cluster.ConvertFrom(s.Current.Cluster.DeepCopy()); err != nil { + return false, errors.Wrap(err, "error converting Cluster to v1beta1 Cluster") + } + + // Call all the registered extension for the hook. + hookRequest := &runtimehooksv1.AfterWorkersUpgradeRequest{ + Cluster: *cleanupV1Beta1Cluster(v1beta1Cluster), + KubernetesVersion: *currentVersion, + ControlPlaneUpgrades: toUpgradeStep(s.UpgradeTracker.ControlPlane.UpgradePlan), + WorkersUpgrades: toUpgradeStep(s.UpgradeTracker.MachineDeployments.UpgradePlan, s.UpgradeTracker.MachinePools.UpgradePlan), + } + hookResponse := &runtimehooksv1.AfterWorkersUpgradeResponse{} + if err := g.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.AfterWorkersUpgrade, s.Current.Cluster, hookRequest, hookResponse); err != nil { + return false, err + } + // Add the response to the tracker so we can later update condition or requeue when required. + s.HookResponseTracker.Add(runtimehooksv1.AfterWorkersUpgrade, hookResponse) + + if hookResponse.RetryAfterSeconds != 0 { + g.hookCache.Add(cache.NewHookEntry(s.Current.Cluster, runtimehooksv1.AfterWorkersUpgrade, time.Now().Add(time.Duration(hookResponse.RetryAfterSeconds)*time.Second), hookResponse.GetMessage())) + log.Info(fmt.Sprintf("Workers upgrade to version %s completed but next steps are blocked by %s hook, retry after %ds", hookRequest.KubernetesVersion, runtimecatalog.HookName(runtimehooksv1.AfterWorkersUpgrade), hookResponse.RetryAfterSeconds), + "ControlPlaneUpgrades", hookRequest.ControlPlaneUpgrades, + "WorkersUpgrades", hookRequest.WorkersUpgrades, + ) + return false, nil + } + if err := hooks.MarkAsDone(ctx, g.Client, s.Current.Cluster, false, runtimehooksv1.AfterWorkersUpgrade); err != nil { + return false, err + } + + log.Info(fmt.Sprintf("Workers upgrade to version %s and %s hook completed, next steps unblocked", hookRequest.KubernetesVersion, runtimecatalog.HookName(runtimehooksv1.AfterWorkersUpgrade)), + "ControlPlaneUpgrades", hookRequest.ControlPlaneUpgrades, + "WorkersUpgrades", hookRequest.WorkersUpgrades, + ) + } + return true, nil +} + +// toUpgradeStep converts a list of version to a list of upgrade steps. +// Note. when called for workers, the function will receive in input two plans one for the MachineDeployments if any, the other for MachinePools if any. +// Considering that both plans, if defined, have to be equal, the function picks the first one not empty. +func toUpgradeStep(plans ...[]string) []runtimehooksv1.UpgradeStepInfo { + var steps []runtimehooksv1.UpgradeStepInfo + for _, plan := range plans { + if len(plan) != 0 { + for _, step := range plan { + steps = append(steps, runtimehooksv1.UpgradeStepInfo{Version: step}) + } + break + } + } + return steps +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/desiredstate/upgrade_plan.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/desiredstate/upgrade_plan.go new file mode 100644 index 0000000000..37223c621f --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/desiredstate/upgrade_plan.go @@ -0,0 +1,490 @@ +/* +Copyright 2025 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 desiredstate + +import ( + "context" + "fmt" + "slices" + "strings" + + "github.com/blang/semver/v4" + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/util/sets" + "sigs.k8s.io/controller-runtime/pkg/client" + + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" + "sigs.k8s.io/cluster-api/exp/topology/scope" + "sigs.k8s.io/cluster-api/feature" + "sigs.k8s.io/cluster-api/internal/contract" + "sigs.k8s.io/cluster-api/util/cache" + "sigs.k8s.io/cluster-api/util/version" +) + +// ComputeUpgradePlan is responsible to computes the upgrade plan for both control plane and workers +// and to set up the upgrade tracker accordingly when there is an upgrade pending. +// +// The upgrade plan for control plane is the result of a pluggable function that should return all the +// intermediates version a control plan upgrade must go through to reach desired version. +// +// The pluggable function could return also upgrade steps for workers; if not, this func +// will determine the minimal number of workers upgrade steps, thus minimizing impact on workloads and reducing the overall upgrade time. +func ComputeUpgradePlan(ctx context.Context, s *scope.Scope, getUpgradePlan GetUpgradePlanFunc) error { + // Return early if control plane is not yet created. + if s.Current.ControlPlane == nil || s.Current.ControlPlane.Object == nil { + return nil + } + + // Get desired version, control plane versions and min worker versions + // NOTE: we consider both machine deployment and machine pools min for computing workers version + // because we are going to ask only a single workers upgrade plan. + desiredVersion := s.Blueprint.Topology.Version + desiredSemVer, err := semver.ParseTolerant(desiredVersion) + if err != nil { + return errors.Wrapf(err, "failed to parse Cluster version %s", desiredVersion) + } + + controlPlaneVersion := "" + v, err := contract.ControlPlane().Version().Get(s.Current.ControlPlane.Object) + if err != nil { + return errors.Wrap(err, "failed to get the version from control plane spec") + } + controlPlaneVersion = *v + controlPlaneSemVer, err := semver.ParseTolerant(*v) + if err != nil { + return errors.Wrapf(err, "failed to parse ControlPlane version %s", *v) + } + + var minWorkersSemVer *semver.Version + for _, md := range s.Current.MachineDeployments { + if md.Object.Spec.Template.Spec.Version != "" { + currentSemVer, err := semver.ParseTolerant(md.Object.Spec.Template.Spec.Version) + if err != nil { + return errors.Wrapf(err, "failed to parse version %s of MachineDeployment %s", md.Object.Spec.Template.Spec.Version, md.Object.Name) + } + if minWorkersSemVer == nil || isLowerThanMinVersion(currentSemVer, *minWorkersSemVer, controlPlaneSemVer) { + minWorkersSemVer = ¤tSemVer + } + } + } + + for _, mp := range s.Current.MachinePools { + if mp.Object.Spec.Template.Spec.Version != "" { + currentSemVer, err := semver.ParseTolerant(mp.Object.Spec.Template.Spec.Version) + if err != nil { + return errors.Wrapf(err, "failed to parse version %s of MachinePool %s", mp.Object.Spec.Template.Spec.Version, mp.Object.Name) + } + if minWorkersSemVer == nil || isLowerThanMinVersion(currentSemVer, *minWorkersSemVer, controlPlaneSemVer) { + minWorkersSemVer = ¤tSemVer + } + } + } + + minWorkersVersion := "" + if minWorkersSemVer != nil { + minWorkersVersion = fmt.Sprintf("v%s", minWorkersSemVer.String()) + } + s.UpgradeTracker.MinWorkersVersion = minWorkersVersion + + // If both control plane and workers are already at the desired version, there is no need to compute the upgrade plan. + if controlPlaneSemVer.String() == desiredSemVer.String() && (minWorkersSemVer == nil || minWorkersSemVer.String() == desiredSemVer.String()) { + return nil + } + + // At this stage we know that an upgrade is required, then call the pluggable func that returns the upgrade plan. + controlPlaneUpgradePlan, workersUpgradePlan, err := getUpgradePlan(ctx, desiredVersion, controlPlaneVersion, minWorkersVersion) + if err != nil { + return err + } + + // DefaultAndValidateUpgradePlans validates both control plane and workers upgrade plan. + // If workers upgrade plan is not specified, default it with the minimal number of workers upgrade steps. + workersUpgradePlan, err = DefaultAndValidateUpgradePlans(desiredVersion, controlPlaneVersion, minWorkersVersion, controlPlaneUpgradePlan, workersUpgradePlan) + if err != nil { + return err + } + + // Sets the control plane upgrade plan. + s.UpgradeTracker.ControlPlane.UpgradePlan = controlPlaneUpgradePlan + + // Sets the machine deployment and workers upgrade plan. + // Note. Each MachineDeployment/MachinePool then has to figure out if/when to pick up the first version in the plan, + // because the minWorkersVersion will be included until all of them are upgraded. + if len(s.Current.MachineDeployments) > 0 { + s.UpgradeTracker.MachineDeployments.UpgradePlan = workersUpgradePlan + } + if len(s.Current.MachinePools) > 0 { + s.UpgradeTracker.MachinePools.UpgradePlan = workersUpgradePlan + } + + return nil +} + +// DefaultAndValidateUpgradePlans validates both control plane and workers upgrade plan. +// If workers upgrade plan is not specified, default it with the minimal number of workers upgrade steps. +func DefaultAndValidateUpgradePlans(desiredVersion string, controlPlaneVersion string, minWorkersVersion string, controlPlaneUpgradePlan []string, workersUpgradePlan []string) ([]string, error) { + desiredSemVer, err := semver.ParseTolerant(desiredVersion) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse Cluster version %s", desiredVersion) + } + + controlPlaneSemVer, err := semver.ParseTolerant(controlPlaneVersion) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse ControlPlane version %s", controlPlaneVersion) + } + + var minWorkersSemVer *semver.Version + if minWorkersVersion != "" { + v, err := semver.ParseTolerant(minWorkersVersion) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse min workers version %s", minWorkersVersion) + } + minWorkersSemVer = &v + } + + // Setup for tracking known version for each minors; this info will be used to build intermediate steps for workers when required + // Note: The control plane might be already one version ahead of workers, we always add current control plane version + // (it should be used as a target version for workers lagging behind). + minors := map[uint64]string{} + minors[controlPlaneSemVer.Minor] = controlPlaneVersion + + // Setup for tracking version order, which is required for disambiguating where there are version with different build numbers + // and thus it is not possible to determine order (and thus the code relies on the version order in the upgrade plan). + versionOrder := map[string]int{} + versionOrder[controlPlaneVersion] = -1 + + // Validate the control plane upgrade plan. + if version.Compare(controlPlaneSemVer, desiredSemVer, version.WithBuildTags()) != 0 { + currentSemVer := controlPlaneSemVer + for i, targetVersion := range controlPlaneUpgradePlan { + versionOrder[targetVersion] = i + targetSemVer, err := semver.ParseTolerant(targetVersion) + if err != nil { + return nil, errors.Wrapf(err, "invalid ControlPlane upgrade plan: item %d; failed to parse version %s", i, targetVersion) + } + + // Check versions in the control plane upgrade plan are in the right order. + // Note: we tolerate having one version followed by another with the same major.minor.patch but different build tags (version.Compare==2) + if version.Compare(targetSemVer, currentSemVer, version.WithBuildTags()) <= 0 { + return nil, errors.Errorf("invalid ControlPlane upgrade plan: item %d; version %s must be greater than v%s", i, targetVersion, currentSemVer) + } + + // Check we are not skipping minors. + if currentSemVer.Minor != targetSemVer.Minor && currentSemVer.Minor+1 != targetSemVer.Minor { + return nil, errors.Errorf("invalid ControlPlane upgrade plan: item %d; expecting a version with minor %d or %d, found version %s", i, currentSemVer.Minor, currentSemVer.Minor+1, targetVersion) + } + + minors[targetSemVer.Minor] = targetVersion + currentSemVer = targetSemVer + } + if version.Compare(currentSemVer, desiredSemVer, version.WithBuildTags()) != 0 { + return nil, errors.Errorf("invalid ControlPlane upgrade plan: control plane upgrade plan must end with version %s, ends with %s instead", desiredVersion, fmt.Sprintf("v%s", currentSemVer)) + } + } else if len(controlPlaneUpgradePlan) > 0 { + return nil, errors.New("invalid ControlPlane upgrade plan: control plane is already at the desired version") + } + + // Defaults and validate the workers upgrade plan. + if minWorkersSemVer != nil && version.Compare(*minWorkersSemVer, desiredSemVer, version.WithBuildTags()) != 0 { + if len(controlPlaneUpgradePlan) > 0 { + // Check that the workers upgrade plan only includes the same versions considered for the control plane upgrade plan, + // plus the control plane version to handle the case that CP already completed its upgrade. + if diff := sets.New(workersUpgradePlan...).Difference(sets.New(controlPlaneUpgradePlan...).Insert(controlPlaneVersion)); len(diff) > 0 { + return nil, errors.Errorf("invalid workers upgrade plan: versions %s doesn't match any versions in the control plane upgrade plan nor the control plane version", strings.Join(diff.UnsortedList(), ",")) + } + } + + // If the workers upgrade plan is empty, default it by adding: + // - upgrade steps whenever required to prevent violation of version skew rules + // - an upgrade step at the end of the upgrade sequence + if len(workersUpgradePlan) == 0 { + currentMinor := minWorkersSemVer.Minor + targetMinor := desiredSemVer.Minor + for i := range targetMinor - currentMinor { + if i > 0 && i%3 == 0 { + targetVersion, ok := minors[currentMinor+i] + if !ok { + // Note: this should never happen, all the minors in the range minWorkersSemVer.Minor-desiredSemVer.Minor should exist in the list of minors, which is + // derived from control plane upgrade plan + current control plane version (a superset of the versions in the workers upgrade plan) + return nil, errors.Wrapf(err, "invalid upgrade plan; unable to identify version for minor %d", currentMinor+i) + } + workersUpgradePlan = append(workersUpgradePlan, targetVersion) + } + } + if len(workersUpgradePlan) == 0 || workersUpgradePlan[len(workersUpgradePlan)-1] != desiredVersion { + workersUpgradePlan = append(workersUpgradePlan, desiredVersion) + } + } + + // Validate the workers upgrade plan. + currentSemVer := *minWorkersSemVer + currentMinor := currentSemVer.Minor + for i, targetVersion := range workersUpgradePlan { + targetSemVer, err := semver.ParseTolerant(targetVersion) + if err != nil { + return nil, errors.Wrapf(err, "invalid workers upgrade plan, item %d; failed to parse version %s", i, targetVersion) + } + + // Check versions in the workers upgrade plan are in the right order. + cmp := version.Compare(targetSemVer, currentSemVer, version.WithBuildTags()) + switch { + case cmp <= 0: + return nil, errors.Errorf("invalid workers upgrade plan, item %d; version %s must be greater than v%s", i, targetVersion, currentSemVer) + case cmp == 2: + // In the case of same major.minor.patch but different build tags (version.Compare==2), check if + // versions are in the same order as in the control plane upgrade plan. + targetVersionOrder, ok := versionOrder[targetVersion] + if !ok { + // Note: this should never happen, all the versions in the workers upgrade plan should exist in versionOrder, which is + // derived from control plane upgrade plan + current control plane version (a superset of the versions in the workers upgrade plan) + return nil, errors.Errorf("invalid workers upgrade plan, item %d; failer to determine version %s order", i, targetVersion) + } + currentVersionOrder, ok := versionOrder[fmt.Sprintf("v%s", currentSemVer)] + if !ok { + // Note: this should never happen, all the versions in the workers upgrade plan should exist in versionOrder, which is + // derived from control plane upgrade plan + current control plane version (a superset of the versions in the workers upgrade plan) + return nil, errors.Errorf("failer to determine version v%s order", currentSemVer) + } + if targetVersionOrder < currentVersionOrder { + return nil, errors.Errorf("invalid workers upgrade plan, item %d; version %s must be before v%s", i, targetVersion, currentSemVer) + } + } + + targetMinor := targetSemVer.Minor + if targetMinor-currentMinor > 3 { + return nil, errors.Errorf("invalid workers upgrade plan, item %d; workers cannot go from minor %d (%s) to minor %d (%s), an intermediate step is required to comply with Kubernetes version skew rules", i, currentMinor, fmt.Sprintf("v%s", currentSemVer.String()), targetMinor, targetVersion) + } + + currentSemVer = targetSemVer + currentMinor = currentSemVer.Minor + } + if version.Compare(currentSemVer, desiredSemVer, version.WithBuildTags()) != 0 { + return nil, errors.Errorf("invalid workers upgrade plan; workers upgrade plan must end with version %s, ends with %s instead", desiredVersion, fmt.Sprintf("v%s", currentSemVer)) + } + } else if len(workersUpgradePlan) > 0 { + return nil, errors.New("invalid worker upgrade plan; there are no workers or workers already at the desired version") + } + + return workersUpgradePlan, nil +} + +func isLowerThanMinVersion(v, minVersion, controlPlaneSemVer semver.Version) bool { + switch cmp := version.Compare(v, minVersion, version.WithBuildTags()); cmp { + case -1: + // v is lower than minVersion + return true + case 2: + // v is different from minVersion, but it is not possible to determine order; + // use control plane version to resolve: MD/MP version can either be equal to control plane version or an older version, + // so v is considered lower than minVersion when different from control plane version. + return v.String() != controlPlaneSemVer.String() + default: + return false + } +} + +// GetUpgradePlanFunc defines the signature for a func that returns the upgrade plan for control plane and workers. +// +// The upgrade plan for control plane must be a list of intermediate version the control plane must go through +// to reach the desired version. The following rules apply: +// - there should be at least one version for every minor between currentControlPlaneVersion (excluded) and desiredVersion (included). +// - each version must be: +// - greater than currentControlPlaneVersion (or with a different build number) +// - greater than the previous version in the list (or with a different build number) +// - less or equal to desiredVersion (or with a different build number) +// - the last version in the plan must be equal to the desired version +// +// The upgrade plan for workers instead in most cases could be left to empty, because the system will automatically +// determine the minimal number of workers upgrade steps, thus minimizing impact on workloads and reducing +// the overall upgrade time. +// +// If instead for any reason the GetUpgradePlanFunc returns a custom upgrade path for workers, the following rules apply: +// - each version must be: +// - equal to currentControlPlaneVersion or to one of the versions in the control plane upgrade plan. +// - greater than current min worker - MachineDeployment & MachinePool - version (or with a different build number) +// - greater than the previous version in the list (or with a different build number) +// - less or equal to the desiredVersion (or with a different build number) +// - in case of versions with the same major/minor/patch version but different build number, also the order +// of those versions must be the same for control plane and worker upgrade plan. +// - the last version in the plan must be equal to the desired version +// - the upgrade plane must have all the intermediate version which workers must go through to avoid breaking rules +// defining the max version skew between control plane and workers. +type GetUpgradePlanFunc func(_ context.Context, desiredVersion, currentControlPlaneVersion, currentMinWorkersVersion string) ([]string, []string, error) + +// GetUpgradePlanOneMinor returns an upgrade plan to reach the next minor. +// The workers upgrade plan will be left empty, thus deferring to ComputeUpgradePlan to compute it. +// NOTE: This is the func the system is going to use when there are no Kubernetes versions or UpgradePlan hook +// defined in the ClusterClass. In this scenario, only upgrade by one minor is supported (same as before implementing chained upgrades). +func GetUpgradePlanOneMinor(_ context.Context, desiredVersion, currentControlPlaneVersion, _ string) ([]string, []string, error) { + desiredSemVer, err := semver.ParseTolerant(desiredVersion) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to parse desired version") + } + + currentControlPlaneSemVer, err := semver.ParseTolerant(currentControlPlaneVersion) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to parse current ControlPlane version") + } + + if currentControlPlaneSemVer.String() == desiredSemVer.String() { + return nil, nil, nil + } + + if desiredSemVer.Minor > currentControlPlaneSemVer.Minor+1 { + return nil, nil, errors.Errorf("cannot compute an upgrade plan from %s to %s", currentControlPlaneVersion, desiredVersion) + } + + return []string{desiredVersion}, nil, nil +} + +// GetUpgradePlanFromClusterClassVersions returns an upgrade plan based on versions defined on a ClusterClass. +// The control plane plan will use the latest version for each minor in between currentControlPlaneVersion and desiredVersion; +// workers upgrade plan will be left empty, thus deferring to ComputeUpgradePlan to compute the most efficient plan. +// NOTE: This is the func the system is going to use when there are Kubernetes versions defined in the ClusterClass. +func GetUpgradePlanFromClusterClassVersions(clusterClassVersions []string) func(_ context.Context, desiredVersion, currentControlPlaneVersion, _ string) ([]string, []string, error) { + return func(_ context.Context, desiredVersion, currentControlPlaneVersion, _ string) ([]string, []string, error) { + desiredSemVer, err := semver.ParseTolerant(desiredVersion) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to parse desired version") + } + + currentControlPlaneSemVer, err := semver.ParseTolerant(currentControlPlaneVersion) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to parse current ControlPlane version") + } + + if currentControlPlaneSemVer.String() == desiredSemVer.String() { + return nil, nil, nil + } + + // Pick all the known kubernetes versions starting from control plane version (excluded) to desired version. + upgradePlan := []string{} + start := false + end := false + for _, v := range clusterClassVersions { + semV, err := semver.ParseTolerant(v) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to parse version %s", v) + } + if (start && !end) || (!start && semV.Minor > currentControlPlaneSemVer.Minor) { + upgradePlan = append(upgradePlan, v) + } + if semV.String() == currentControlPlaneSemVer.String() || version.Compare(currentControlPlaneSemVer, semV, version.WithBuildTags()) < 0 { + start = true + } + if semV.String() == desiredSemVer.String() || version.Compare(desiredSemVer, semV, version.WithBuildTags()) < 0 { + end = true + } + } + + if len(upgradePlan) == 0 { + return upgradePlan, nil, nil + } + + // In case there is more than one version for one minor, drop all the versions for one minor except the last. + simplifiedUpgradePlan := []string{} + currentMinor := currentControlPlaneSemVer.Minor + + // Note: Add the current minor version if the upgradePlan only upgrades patch versions. + // In this case the `semV.Minor > currentMinor` check below would not add the minor. + if currentMinor == desiredSemVer.Minor { + simplifiedUpgradePlan = append(simplifiedUpgradePlan, upgradePlan[0]) + } + for _, v := range upgradePlan { + semV, err := semver.ParseTolerant(v) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to parse version %s", v) + } + if semV.Minor > currentMinor { + simplifiedUpgradePlan = append(simplifiedUpgradePlan, v) + } + if semV.Minor == currentMinor && len(simplifiedUpgradePlan) > 0 { + simplifiedUpgradePlan[len(simplifiedUpgradePlan)-1] = v + } + currentMinor = semV.Minor + } + return simplifiedUpgradePlan, nil, nil + } +} + +// GenerateUpgradePlanCacheEntry is an entry for the GenerateUpgradePlan hook cache. +type GenerateUpgradePlanCacheEntry struct { + ClusterKey client.ObjectKey + FromControlPlaneKubernetesVersion string + FromWorkersKubernetesVersion string + ToKubernetesVersion string + ControlPlaneUpgradePlan []string + WorkersUpgradePlan []string +} + +// Key returns the cache key of a GenerateUpgradePlanCacheEntry. +func (r GenerateUpgradePlanCacheEntry) Key() string { + return fmt.Sprintf("%s: (%s,%s) => %s", r.ClusterKey, r.FromControlPlaneKubernetesVersion, r.FromWorkersKubernetesVersion, r.ToKubernetesVersion) +} + +// GetUpgradePlanFromExtension returns an upgrade plan by calling the GenerateUpgradePlan runtime extension. +func GetUpgradePlanFromExtension(runtimeClient runtimeclient.Client, getUpgradePlanCache cache.Cache[GenerateUpgradePlanCacheEntry], cluster *clusterv1.Cluster, extensionName string) func(ctx context.Context, desiredVersion, currentControlPlaneVersion, currentMinWorkersVersion string) ([]string, []string, error) { + return func(ctx context.Context, desiredVersion, currentControlPlaneVersion, currentMinWorkersVersion string) ([]string, []string, error) { + if !feature.Gates.Enabled(feature.RuntimeSDK) { + return nil, nil, errors.Errorf("can not use GenerateUpgradePlan extension %q if RuntimeSDK feature flag is disabled", extensionName) + } + + // Prepare the request. + req := &runtimehooksv1.GenerateUpgradePlanRequest{ + Cluster: *cleanupCluster(cluster.DeepCopy()), + FromControlPlaneKubernetesVersion: currentControlPlaneVersion, + FromWorkersKubernetesVersion: currentMinWorkersVersion, + ToKubernetesVersion: desiredVersion, + } + + entry := GenerateUpgradePlanCacheEntry{ + ClusterKey: client.ObjectKeyFromObject(cluster), + FromControlPlaneKubernetesVersion: req.FromControlPlaneKubernetesVersion, + FromWorkersKubernetesVersion: req.FromWorkersKubernetesVersion, + ToKubernetesVersion: req.ToKubernetesVersion, + } + + if cacheEntry, ok := getUpgradePlanCache.Has(entry.Key()); ok { + return slices.Clone(cacheEntry.ControlPlaneUpgradePlan), slices.Clone(cacheEntry.WorkersUpgradePlan), nil + } + + // Call the extension. + resp := &runtimehooksv1.GenerateUpgradePlanResponse{} + if err := runtimeClient.CallExtension(ctx, runtimehooksv1.GenerateUpgradePlan, cluster, extensionName, req, resp); err != nil { + return nil, nil, errors.Wrap(err, "failed to get upgrade plan from extension") + } + + // Convert UpgradeStep to string slice. + controlPlaneUpgradePlan := make([]string, len(resp.ControlPlaneUpgrades)) + for i, step := range resp.ControlPlaneUpgrades { + controlPlaneUpgradePlan[i] = step.Version + } + + workersUpgradePlan := make([]string, len(resp.WorkersUpgrades)) + for i, step := range resp.WorkersUpgrades { + workersUpgradePlan[i] = step.Version + } + + entry.ControlPlaneUpgradePlan = slices.Clone(controlPlaneUpgradePlan) + entry.WorkersUpgradePlan = slices.Clone(workersUpgradePlan) + getUpgradePlanCache.Add(entry) + + return controlPlaneUpgradePlan, workersUpgradePlan, nil + } +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/scope/blueprint.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/scope/blueprint.go index 15c20fe3c5..5a6786519d 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/scope/blueprint.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/scope/blueprint.go @@ -118,8 +118,9 @@ func (b *ClusterBlueprint) IsControlPlaneMachineHealthCheckEnabled() bool { func (b *ClusterBlueprint) ControlPlaneMachineHealthCheckClass() (clusterv1.MachineHealthCheckChecks, clusterv1.MachineHealthCheckRemediation) { if b.Topology.ControlPlane.HealthCheck.IsDefined() { return clusterv1.MachineHealthCheckChecks{ - NodeStartupTimeoutSeconds: b.Topology.ControlPlane.HealthCheck.Checks.NodeStartupTimeoutSeconds, - UnhealthyNodeConditions: b.Topology.ControlPlane.HealthCheck.Checks.UnhealthyNodeConditions, + NodeStartupTimeoutSeconds: b.Topology.ControlPlane.HealthCheck.Checks.NodeStartupTimeoutSeconds, + UnhealthyNodeConditions: b.Topology.ControlPlane.HealthCheck.Checks.UnhealthyNodeConditions, + UnhealthyMachineConditions: b.Topology.ControlPlane.HealthCheck.Checks.UnhealthyMachineConditions, }, clusterv1.MachineHealthCheckRemediation{ TriggerIf: clusterv1.MachineHealthCheckRemediationTriggerIf{ UnhealthyLessThanOrEqualTo: b.Topology.ControlPlane.HealthCheck.Remediation.TriggerIf.UnhealthyLessThanOrEqualTo, @@ -130,8 +131,9 @@ func (b *ClusterBlueprint) ControlPlaneMachineHealthCheckClass() (clusterv1.Mach } return clusterv1.MachineHealthCheckChecks{ - NodeStartupTimeoutSeconds: b.ControlPlane.HealthCheck.Checks.NodeStartupTimeoutSeconds, - UnhealthyNodeConditions: b.ControlPlane.HealthCheck.Checks.UnhealthyNodeConditions, + NodeStartupTimeoutSeconds: b.ControlPlane.HealthCheck.Checks.NodeStartupTimeoutSeconds, + UnhealthyNodeConditions: b.ControlPlane.HealthCheck.Checks.UnhealthyNodeConditions, + UnhealthyMachineConditions: b.ControlPlane.HealthCheck.Checks.UnhealthyMachineConditions, }, clusterv1.MachineHealthCheckRemediation{ TriggerIf: clusterv1.MachineHealthCheckRemediationTriggerIf{ UnhealthyLessThanOrEqualTo: b.ControlPlane.HealthCheck.Remediation.TriggerIf.UnhealthyLessThanOrEqualTo, @@ -165,8 +167,9 @@ func (b *ClusterBlueprint) IsMachineDeploymentMachineHealthCheckEnabled(md *clus func (b *ClusterBlueprint) MachineDeploymentMachineHealthCheckClass(md *clusterv1.MachineDeploymentTopology) (clusterv1.MachineHealthCheckChecks, clusterv1.MachineHealthCheckRemediation) { if md.HealthCheck.IsDefined() { return clusterv1.MachineHealthCheckChecks{ - NodeStartupTimeoutSeconds: md.HealthCheck.Checks.NodeStartupTimeoutSeconds, - UnhealthyNodeConditions: md.HealthCheck.Checks.UnhealthyNodeConditions, + NodeStartupTimeoutSeconds: md.HealthCheck.Checks.NodeStartupTimeoutSeconds, + UnhealthyNodeConditions: md.HealthCheck.Checks.UnhealthyNodeConditions, + UnhealthyMachineConditions: md.HealthCheck.Checks.UnhealthyMachineConditions, }, clusterv1.MachineHealthCheckRemediation{ TriggerIf: clusterv1.MachineHealthCheckRemediationTriggerIf{ UnhealthyLessThanOrEqualTo: md.HealthCheck.Remediation.TriggerIf.UnhealthyLessThanOrEqualTo, @@ -177,8 +180,9 @@ func (b *ClusterBlueprint) MachineDeploymentMachineHealthCheckClass(md *clusterv } return clusterv1.MachineHealthCheckChecks{ - NodeStartupTimeoutSeconds: b.MachineDeployments[md.Class].HealthCheck.Checks.NodeStartupTimeoutSeconds, - UnhealthyNodeConditions: b.MachineDeployments[md.Class].HealthCheck.Checks.UnhealthyNodeConditions, + NodeStartupTimeoutSeconds: b.MachineDeployments[md.Class].HealthCheck.Checks.NodeStartupTimeoutSeconds, + UnhealthyNodeConditions: b.MachineDeployments[md.Class].HealthCheck.Checks.UnhealthyNodeConditions, + UnhealthyMachineConditions: b.MachineDeployments[md.Class].HealthCheck.Checks.UnhealthyMachineConditions, }, clusterv1.MachineHealthCheckRemediation{ TriggerIf: clusterv1.MachineHealthCheckRemediationTriggerIf{ UnhealthyLessThanOrEqualTo: b.MachineDeployments[md.Class].HealthCheck.Remediation.TriggerIf.UnhealthyLessThanOrEqualTo, diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/scope/hookresponsetracker.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/scope/hookresponsetracker.go index e822961fda..c8201ea3e6 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/scope/hookresponsetracker.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/scope/hookresponsetracker.go @@ -64,6 +64,11 @@ func (h *HookResponseTracker) IsBlocking(hook runtimecatalog.Hook) bool { return true } +// IsAnyBlocking return true if at least one hook is blocking. +func (h *HookResponseTracker) IsAnyBlocking() bool { + return h.AggregateRetryAfter() > 0 +} + // AggregateRetryAfter calculates the lowest non-zero retryAfterSeconds time from all the tracked responses. func (h *HookResponseTracker) AggregateRetryAfter() time.Duration { res := int32(0) @@ -76,7 +81,7 @@ func (h *HookResponseTracker) AggregateRetryAfter() time.Duration { } // AggregateMessage returns a human friendly message about the blocking status of hooks. -func (h *HookResponseTracker) AggregateMessage() string { +func (h *HookResponseTracker) AggregateMessage(action string) string { blockingHooks := map[string]string{} for hook, resp := range h.responses { if retryResponse, ok := resp.(runtimehooksv1.RetryResponseObject); ok { @@ -92,10 +97,10 @@ func (h *HookResponseTracker) AggregateMessage() string { hookAndMessages := []string{} for hook, message := range blockingHooks { if message == "" { - hookAndMessages = append(hookAndMessages, fmt.Sprintf("hook %q is blocking", hook)) + hookAndMessages = append(hookAndMessages, hook) } else { - hookAndMessages = append(hookAndMessages, fmt.Sprintf("hook %q is blocking: %s", hook, message)) + hookAndMessages = append(hookAndMessages, fmt.Sprintf("%s: %s", hook, message)) } } - return strings.Join(hookAndMessages, "; ") + return fmt.Sprintf("Following hooks are blocking %s: %s", action, strings.Join(hookAndMessages, "; ")) } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/scope/upgradetracker.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/scope/upgradetracker.go index a13cbbf6ef..fef4649a1e 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/scope/upgradetracker.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/topology/scope/upgradetracker.go @@ -23,6 +23,7 @@ type UpgradeTracker struct { ControlPlane ControlPlaneUpgradeTracker MachineDeployments WorkerUpgradeTracker MachinePools WorkerUpgradeTracker + MinWorkersVersion string } // ControlPlaneUpgradeTracker holds the current upgrade status of the Control Plane. @@ -36,6 +37,23 @@ type ControlPlaneUpgradeTracker struct { // - Upgrade is blocked because any of the current MachineDeployments or MachinePools are upgrading. IsPendingUpgrade bool + // IsWaitingForWorkersUpgrade documents when a Control Plane is pending a version upgrade but + // it cannot pick up the new version until workers upgrades. + // Note: this happens when performing a multistep upgrade, and the current upgrade step requires + // also workers to upgrade, e.g. for preventing violation of the rule that defines the max + // version skew between control plane and workers. + IsWaitingForWorkersUpgrade bool + + // UpgradePlan tracks the list of version upgrades required to reach the desired version. + // The following rules apply: + // - there should be at least one version for every minor between currentControlPlaneVersion (excluded) and desiredVersion (included). + // - each version must be: + // - greater than currentControlPlaneVersion (or with a different build number) + // - greater than the previous version in the list (or with a different build number) + // - less or equal to desiredVersion (or with a different build number) + // - the last version in the plan must be equal to the desired version + UpgradePlan []string + // IsProvisioning is true if the current Control Plane is being provisioned for the first time. False otherwise. IsProvisioning bool @@ -70,6 +88,20 @@ type WorkerUpgradeTracker struct { // By marking a MachineDeployment/MachinePool as pendingUpgrade we skip reconciling the MachineDeployment/MachinePool. pendingUpgradeNames sets.Set[string] + // UpgradePlan tracks the list of version upgrades required to reach the desired version. + // the following rules apply: + // - each version must be: + // - equal to currentControlPlaneVersion or to one of the versions in the control plane upgrade plan. + // - greater than current min worker - MachineDeployment & MachinePool - version (or with a different build number) + // - greater than the previous version in the list (or with a different build number) + // - less or equal to the desiredVersion (or with a different build number) + // - in case of versions with the same major/minor/patch version but different build number, also the order + // of those versions must be the same for control plane and worker upgrade plan. + // - the last version in the plan must be equal to the desired version + // - the upgrade plane must have all the intermediate version which workers must go through to avoid breaking rules + // defining the max version skew between control plane and workers. + UpgradePlan []string + // deferredNames is the set of MachineDeployment/MachinePool names that are not going to pick up the new version // in the current reconcile loop because they are deferred by the user. // Note: If a MachineDeployment/MachinePool is marked as deferred it should also be marked as pendingUpgrade. @@ -179,6 +211,33 @@ func (t *ControlPlaneUpgradeTracker) IsControlPlaneStable() bool { return true } +// IsControlPlaneStableOrWaitingForWorkersUpgrade returns true is the ControlPlane is stable or waiting for worker upgrade. +func (t *ControlPlaneUpgradeTracker) IsControlPlaneStableOrWaitingForWorkersUpgrade() bool { + // If the current control plane is provisioning it is not considered stable. + if t.IsProvisioning { + return false + } + + // If the current control plane is upgrading it is not considered stable. + if t.IsUpgrading { + return false + } + + // Check if we are about to upgrade the control plane. Since the control plane is about to start its upgrade process + // it cannot be considered stable. + if t.IsStartingUpgrade { + return false + } + + // If the ControlPlane is pending picking up an upgrade then it is not yet at the desired state and + // cannot be considered stable unless the control plane is waiting for worker upgrade. + if t.IsPendingUpgrade && !t.IsWaitingForWorkersUpgrade { + return false + } + + return true +} + // MarkUpgrading marks a MachineDeployment/MachinePool as currently upgrading or about to upgrade. func (m *WorkerUpgradeTracker) MarkUpgrading(names ...string) { for _, name := range names { @@ -192,6 +251,12 @@ func (m *WorkerUpgradeTracker) UpgradingNames() []string { return sets.List(m.upgradingNames) } +// IsAnyUpgrading returns true if any of the machine deployments are upgrading. +// Returns false, otherwise. +func (m *WorkerUpgradeTracker) IsAnyUpgrading() bool { + return len(m.upgradingNames) != 0 +} + // UpgradeConcurrencyReached returns true if the number of MachineDeployments/MachinePools upgrading is at the concurrency limit. func (m *WorkerUpgradeTracker) UpgradeConcurrencyReached() bool { return m.upgradingNames.Len() >= m.maxUpgradeConcurrency @@ -257,8 +322,8 @@ func (m *WorkerUpgradeTracker) DeferredUpgradeNames() []string { return sets.List(m.deferredNames) } -// DeferredUpgrade returns true if the upgrade has been deferred for any of the +// IsAnyUpgradeDeferred returns true if the upgrade has been deferred for any of the // MachineDeployments/MachinePools. Returns false, otherwise. -func (m *WorkerUpgradeTracker) DeferredUpgrade() bool { +func (m *WorkerUpgradeTracker) IsAnyUpgradeDeferred() bool { return len(m.deferredNames) != 0 } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/util/util.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/util/util.go deleted file mode 100644 index 5235cb3c63..0000000000 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/util/util.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2020 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 util implements utility functions. -package util - -import ( - "context" - - "github.com/pkg/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/klog/v2" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" - "sigs.k8s.io/cluster-api/util/labels/format" -) - -// GetOwnerMachinePool returns the MachinePool objects owning the current resource. -func GetOwnerMachinePool(ctx context.Context, c client.Client, obj metav1.ObjectMeta) (*clusterv1.MachinePool, error) { - for _, ref := range obj.GetOwnerReferences() { - if ref.Kind != "MachinePool" { - continue - } - gv, err := schema.ParseGroupVersion(ref.APIVersion) - if err != nil { - return nil, errors.WithStack(err) - } - if gv.Group == clusterv1.GroupVersion.Group { - return GetMachinePoolByName(ctx, c, obj.Namespace, ref.Name) - } - } - return nil, nil -} - -// GetMachinePoolByName finds and returns a MachinePool object using the specified params. -func GetMachinePoolByName(ctx context.Context, c client.Client, namespace, name string) (*clusterv1.MachinePool, error) { - m := &clusterv1.MachinePool{} - key := client.ObjectKey{Name: name, Namespace: namespace} - if err := c.Get(ctx, key, m); err != nil { - return nil, err - } - return m, nil -} - -// GetMachinePoolByLabels finds and returns a MachinePool object using the value of clusterv1.MachinePoolNameLabel. -// This differs from GetMachinePoolByName as the label value can be a hash. -func GetMachinePoolByLabels(ctx context.Context, c client.Client, namespace string, labels map[string]string) (*clusterv1.MachinePool, error) { - selector := map[string]string{} - if clusterName, ok := labels[clusterv1.ClusterNameLabel]; ok { - selector = map[string]string{clusterv1.ClusterNameLabel: clusterName} - } - - if poolNameHash, ok := labels[clusterv1.MachinePoolNameLabel]; ok { - machinePoolList := &clusterv1.MachinePoolList{} - if err := c.List(ctx, machinePoolList, client.InNamespace(namespace), client.MatchingLabels(selector)); err != nil { - return nil, errors.Wrapf(err, "failed to list MachinePools using labels %v", selector) - } - - for _, mp := range machinePoolList.Items { - if format.MustFormatValue(mp.Name) == poolNameHash { - return &mp, nil - } - } - } else { - return nil, errors.Errorf("labels missing required key `%s`", clusterv1.MachinePoolNameLabel) - } - - return nil, nil -} - -// MachinePoolToInfrastructureMapFunc returns a handler.MapFunc that watches for -// MachinePool events and returns reconciliation requests for an infrastructure provider object. -func MachinePoolToInfrastructureMapFunc(ctx context.Context, gvk schema.GroupVersionKind) handler.MapFunc { - log := ctrl.LoggerFrom(ctx) - return func(_ context.Context, o client.Object) []reconcile.Request { - m, ok := o.(*clusterv1.MachinePool) - if !ok { - log.V(4).Info("Not a machine pool", "Object", klog.KObj(o)) - return nil - } - log := log.WithValues("MachinePool", klog.KObj(o)) - - gk := gvk.GroupKind() - ref := m.Spec.Template.Spec.InfrastructureRef - // Return early if the GroupKind doesn't match what we expect. - infraGK := ref.GroupKind() - if gk != infraGK { - log.V(4).Info("Infra kind doesn't match filter group kind", "infrastructureGroupKind", infraGK.String()) - return nil - } - - log.V(4).Info("Projecting object") - return []reconcile.Request{ - { - NamespacedName: client.ObjectKey{ - Namespace: m.Namespace, - Name: ref.Name, - }, - }, - } - } -} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/webhooks/alias.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/webhooks/alias.go deleted file mode 100644 index 21deef6e76..0000000000 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/webhooks/alias.go +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2023 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 webhooks - -import ( - ctrl "sigs.k8s.io/controller-runtime" - - "sigs.k8s.io/cluster-api/exp/internal/webhooks" -) - -// MachinePool implements a validating and defaulting webhook for MachinePool. -type MachinePool struct{} - -// SetupWebhookWithManager sets up MachinePool webhooks. -func (webhook *MachinePool) SetupWebhookWithManager(mgr ctrl.Manager) error { - return (&webhooks.MachinePool{}).SetupWebhookWithManager(mgr) -} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/webhooks/doc.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/webhooks/doc.go deleted file mode 100644 index ad3cd005e8..0000000000 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/webhooks/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2023 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 webhooks contains external webhook implementations for some of our API types. -package webhooks diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/feature/feature.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/feature/feature.go index 15a6fb169b..da8dd8e8da 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/feature/feature.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/feature/feature.go @@ -35,15 +35,6 @@ const ( // beta: v1.7 MachinePool featuregate.Feature = "MachinePool" - // ClusterResourceSet is a feature gate for the ClusterResourceSet functionality. - // - // alpha: v0.3 - // beta: v0.4 - // GA: v1.10 - // - // Deprecated: ClusterResourceSet feature is now GA and the corresponding feature flag will be removed in 1.12 release. - ClusterResourceSet featuregate.Feature = "ClusterResourceSet" - // ClusterTopology is a feature gate for the ClusterClass and managed topologies functionality. // // alpha: v0.4 @@ -77,6 +68,22 @@ const ( // // alpha: v1.10 PriorityQueue featuregate.Feature = "PriorityQueue" + + // ReconcilerRateLimiting is a feature gate that controls if reconcilers are rate-limited. + // Note: Currently the feature gate is rate-limiting to 1 request / 1 second. + // Note: If this feature gate is enabled the PriorityQueue feature gate must be enabled as well. + // + // alpha: v1.12 + ReconcilerRateLimiting featuregate.Feature = "ReconcilerRateLimiting" + + // InPlaceUpdates is a feature gate for the in-place machine updates functionality. + // alpha: v1.12 + InPlaceUpdates featuregate.Feature = "InPlaceUpdates" + + // MachineTaintPropagation is a feature gate for the machine taint propagation functionality. + // + // alpha: v1.12 + MachineTaintPropagation featuregate.Feature = "MachineTaintPropagation" ) func init() { @@ -87,12 +94,14 @@ func init() { // To add a new feature, define a key for it above and add it here. var defaultClusterAPIFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ // Every feature should be initiated here: - ClusterResourceSet: {Default: true, PreRelease: featuregate.GA}, MachinePool: {Default: true, PreRelease: featuregate.Beta}, MachineSetPreflightChecks: {Default: true, PreRelease: featuregate.Beta}, MachineWaitForVolumeDetachConsiderVolumeAttachments: {Default: true, PreRelease: featuregate.Beta}, PriorityQueue: {Default: false, PreRelease: featuregate.Alpha}, + ReconcilerRateLimiting: {Default: false, PreRelease: featuregate.Alpha}, ClusterTopology: {Default: false, PreRelease: featuregate.Alpha}, KubeadmBootstrapFormatIgnition: {Default: false, PreRelease: featuregate.Alpha}, RuntimeSDK: {Default: false, PreRelease: featuregate.Alpha}, + InPlaceUpdates: {Default: false, PreRelease: featuregate.Alpha}, + MachineTaintPropagation: {Default: false, PreRelease: featuregate.Alpha}, } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha3/conversion.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha3/conversion.go index 8cda6f6a25..4f08b27ab7 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha3/conversion.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha3/conversion.go @@ -246,6 +246,7 @@ func (src *Machine) ConvertTo(dstRaw conversion.Hub) error { if ok { dst.Spec.MinReadySeconds = restored.Spec.MinReadySeconds dst.Spec.ReadinessGates = restored.Spec.ReadinessGates + dst.Spec.Taints = restored.Spec.Taints dst.Spec.Deletion.NodeDeletionTimeoutSeconds = restored.Spec.Deletion.NodeDeletionTimeoutSeconds dst.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = restored.Spec.Deletion.NodeVolumeDetachTimeoutSeconds dst.Status.NodeInfo = restored.Status.NodeInfo @@ -333,6 +334,7 @@ func (src *MachineSet) ConvertTo(dstRaw conversion.Hub) error { return err } dst.Spec.Template.Spec.ReadinessGates = restored.Spec.Template.Spec.ReadinessGates + dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints dst.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds dst.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds if restored.Status.Deprecated != nil && restored.Status.Deprecated.V1Beta1 != nil { @@ -425,6 +427,7 @@ func (src *MachineDeployment) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.Remediation = restored.Spec.Remediation dst.Spec.MachineNaming = restored.Spec.MachineNaming dst.Spec.Template.Spec.ReadinessGates = restored.Spec.Template.Spec.ReadinessGates + dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints dst.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds dst.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds dst.Spec.Rollout.After = restored.Spec.Rollout.After @@ -503,6 +506,8 @@ func (src *MachineHealthCheck) ConvertTo(dstRaw conversion.Hub) error { return err } + dst.Spec.Checks.UnhealthyMachineConditions = restored.Spec.Checks.UnhealthyMachineConditions + clusterv1.Convert_int32_To_Pointer_int32(src.Status.ExpectedMachines, ok, restored.Status.ExpectedMachines, &dst.Status.ExpectedMachines) clusterv1.Convert_int32_To_Pointer_int32(src.Status.CurrentHealthy, ok, restored.Status.CurrentHealthy, &dst.Status.CurrentHealthy) clusterv1.Convert_int32_To_Pointer_int32(src.Status.RemediationsAllowed, ok, restored.Status.RemediationsAllowed, &dst.Status.RemediationsAllowed) @@ -601,6 +606,7 @@ func (src *MachinePool) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.Template.Spec.ReadinessGates = restored.Spec.Template.Spec.ReadinessGates dst.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds dst.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds + dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints dst.Status.Conditions = restored.Status.Conditions dst.Status.AvailableReplicas = restored.Status.AvailableReplicas dst.Status.ReadyReplicas = restored.Status.ReadyReplicas diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha3/zz_generated.conversion.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha3/zz_generated.conversion.go index 0c9c762839..532cb03ea7 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha3/zz_generated.conversion.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha3/zz_generated.conversion.go @@ -1335,6 +1335,7 @@ func autoConvert_v1beta2_MachineSpec_To_v1alpha3_MachineSpec(in *v1beta2.Machine // WARNING: in.MinReadySeconds requires manual conversion: does not exist in peer-type // WARNING: in.ReadinessGates requires manual conversion: does not exist in peer-type // WARNING: in.Deletion requires manual conversion: does not exist in peer-type + // WARNING: in.Taints requires manual conversion: does not exist in peer-type return nil } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha4/conversion.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha4/conversion.go index 6e9ee3e880..112662de73 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha4/conversion.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha4/conversion.go @@ -248,6 +248,7 @@ func (src *ClusterClass) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.ControlPlane.Deletion.NodeVolumeDetachTimeoutSeconds = restored.Spec.ControlPlane.Deletion.NodeVolumeDetachTimeoutSeconds dst.Spec.ControlPlane.Deletion.NodeDeletionTimeoutSeconds = restored.Spec.ControlPlane.Deletion.NodeDeletionTimeoutSeconds dst.Spec.Workers.MachinePools = restored.Spec.Workers.MachinePools + dst.Spec.KubernetesVersions = restored.Spec.KubernetesVersions for i := range restored.Spec.Workers.MachineDeployments { dst.Spec.Workers.MachineDeployments[i].HealthCheck = restored.Spec.Workers.MachineDeployments[i].HealthCheck @@ -262,6 +263,7 @@ func (src *ClusterClass) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.Workers.MachineDeployments[i].Rollout.Strategy = restored.Spec.Workers.MachineDeployments[i].Rollout.Strategy } dst.Status = restored.Status + dst.Spec.Upgrade.External.GenerateUpgradePlanExtension = restored.Spec.Upgrade.External.GenerateUpgradePlanExtension return nil } @@ -331,6 +333,7 @@ func (src *Machine) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.Deletion.NodeDeletionTimeoutSeconds = restored.Spec.Deletion.NodeDeletionTimeoutSeconds dst.Status.CertificatesExpiryDate = restored.Status.CertificatesExpiryDate dst.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = restored.Spec.Deletion.NodeVolumeDetachTimeoutSeconds + dst.Spec.Taints = restored.Spec.Taints dst.Status.Deletion = restored.Status.Deletion dst.Status.Conditions = restored.Status.Conditions } @@ -420,6 +423,7 @@ func (src *MachineSet) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.Template.Spec.ReadinessGates = restored.Spec.Template.Spec.ReadinessGates dst.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds dst.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds + dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints dst.Status.Conditions = restored.Status.Conditions dst.Status.AvailableReplicas = restored.Status.AvailableReplicas dst.Status.ReadyReplicas = restored.Status.ReadyReplicas @@ -516,6 +520,7 @@ func (src *MachineDeployment) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.Rollout.After = restored.Spec.Rollout.After dst.Spec.Remediation = restored.Spec.Remediation dst.Spec.MachineNaming = restored.Spec.MachineNaming + dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints dst.Status.Conditions = restored.Status.Conditions dst.Status.AvailableReplicas = restored.Status.AvailableReplicas dst.Status.ReadyReplicas = restored.Status.ReadyReplicas @@ -591,6 +596,8 @@ func (src *MachineHealthCheck) ConvertTo(dstRaw conversion.Hub) error { return err } + dst.Spec.Checks.UnhealthyMachineConditions = restored.Spec.Checks.UnhealthyMachineConditions + clusterv1.Convert_int32_To_Pointer_int32(src.Status.ExpectedMachines, ok, restored.Status.ExpectedMachines, &dst.Status.ExpectedMachines) clusterv1.Convert_int32_To_Pointer_int32(src.Status.CurrentHealthy, ok, restored.Status.CurrentHealthy, &dst.Status.CurrentHealthy) clusterv1.Convert_int32_To_Pointer_int32(src.Status.RemediationsAllowed, ok, restored.Status.RemediationsAllowed, &dst.Status.RemediationsAllowed) @@ -682,6 +689,7 @@ func (src *MachinePool) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.Template.Spec.ReadinessGates = restored.Spec.Template.Spec.ReadinessGates dst.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds dst.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds + dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints dst.Status.Conditions = restored.Status.Conditions dst.Status.AvailableReplicas = restored.Status.AvailableReplicas dst.Status.ReadyReplicas = restored.Status.ReadyReplicas diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha4/zz_generated.conversion.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha4/zz_generated.conversion.go index 308061fec5..c807a20e06 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha4/zz_generated.conversion.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/api/core/v1alpha4/zz_generated.conversion.go @@ -683,6 +683,8 @@ func autoConvert_v1beta2_ClusterClassSpec_To_v1alpha4_ClusterClassSpec(in *v1bet } // WARNING: in.Variables requires manual conversion: does not exist in peer-type // WARNING: in.Patches requires manual conversion: does not exist in peer-type + // WARNING: in.Upgrade requires manual conversion: does not exist in peer-type + // WARNING: in.KubernetesVersions requires manual conversion: does not exist in peer-type return nil } @@ -1724,6 +1726,7 @@ func autoConvert_v1beta2_MachineSpec_To_v1alpha4_MachineSpec(in *v1beta2.Machine // WARNING: in.MinReadySeconds requires manual conversion: does not exist in peer-type // WARNING: in.ReadinessGates requires manual conversion: does not exist in peer-type // WARNING: in.Deletion requires manual conversion: does not exist in peer-type + // WARNING: in.Taints requires manual conversion: does not exist in peer-type return nil } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/contract/controlplane.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/contract/controlplane.go index 3a14238c91..3d08159b77 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/contract/controlplane.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/contract/controlplane.go @@ -54,6 +54,33 @@ func (c *ControlPlaneContract) MachineTemplate() *ControlPlaneMachineTemplate { return &ControlPlaneMachineTemplate{} } +// IgnorePaths returns a list of paths to be ignored when reconciling an ControlPlane. +// NOTE: The controlPlaneEndpoint struct currently contains two mandatory fields (host and port). +// As the host and port fields are not using omitempty, they are automatically set to their zero values +// if they are not set by the user. We don't want to reconcile the zero values as we would then overwrite +// changes applied by the infrastructure provider controller. +func (c *ControlPlaneContract) IgnorePaths(controlPlane *unstructured.Unstructured) ([]Path, error) { + var ignorePaths []Path + + host, ok, err := unstructured.NestedString(controlPlane.UnstructuredContent(), ControlPlane().ControlPlaneEndpoint().host().Path()...) + if err != nil { + return nil, errors.Wrapf(err, "failed to retrieve %s", ControlPlane().ControlPlaneEndpoint().host().Path().String()) + } + if ok && host == "" { + ignorePaths = append(ignorePaths, ControlPlane().ControlPlaneEndpoint().host().Path()) + } + + port, ok, err := unstructured.NestedInt64(controlPlane.UnstructuredContent(), ControlPlane().ControlPlaneEndpoint().port().Path()...) + if err != nil { + return nil, errors.Wrapf(err, "failed to retrieve %s", ControlPlane().ControlPlaneEndpoint().port().Path().String()) + } + if ok && port == 0 { + ignorePaths = append(ignorePaths, ControlPlane().ControlPlaneEndpoint().port().Path()) + } + + return ignorePaths, nil +} + // Version provide access to version field in a ControlPlane object, if any. // NOTE: When working with unstructured there is no way to understand if the ControlPlane provider // do support a field in the type definition from the fact that a field is not set in a given instance. @@ -75,7 +102,7 @@ func (c *ControlPlaneContract) StatusVersion() *String { func (c *ControlPlaneContract) Initialized(contractVersion string) *Bool { if contractVersion == "v1beta1" { return &Bool{ - path: []string{"status", "ready"}, + path: []string{"status", "initialized"}, } } @@ -187,7 +214,7 @@ func (c *ControlPlaneContract) ExternalManagedControlPlane() *Bool { // IsProvisioning returns true if the control plane is being created for the first time. // Returns false, if the control plane was already previously provisioned. func (c *ControlPlaneContract) IsProvisioning(obj *unstructured.Unstructured) (bool, error) { - // We can know if the control plane was previously created or is being cretaed for the first + // We can know if the control plane was previously created or is being created for the first // time by looking at controlplane.status.version. If the version in status is set to a valid // value then the control plane was already provisioned at a previous time. If not, we can // assume that the control plane is being created for the first time. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/cluster/cluster_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/cluster/cluster_controller.go index 7e66695bdc..ff54eca90d 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/cluster/cluster_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/cluster/cluster_controller.go @@ -34,7 +34,6 @@ import ( "k8s.io/client-go/tools/record" "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -47,6 +46,7 @@ import ( "sigs.k8s.io/cluster-api/controllers/external" "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/hooks" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/collections" "sigs.k8s.io/cluster-api/util/conditions" @@ -94,7 +94,7 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt } predicateLog := ctrl.LoggerFrom(ctx).WithValues("controller", "cluster") - b := ctrl.NewControllerManagedBy(mgr). + b := capicontrollerutil.NewControllerManagedBy(mgr, predicateLog). For(&clusterv1.Cluster{}). WatchesRawSource(r.ClusterCache.GetClusterSource("cluster", func(_ context.Context, o client.Object) []ctrl.Request { return []ctrl.Request{{NamespacedName: client.ObjectKeyFromObject(o)}} @@ -102,18 +102,15 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt Watches( &clusterv1.Machine{}, handler.EnqueueRequestsFromMapFunc(r.controlPlaneMachineToCluster), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog)), ). Watches( &clusterv1.MachineDeployment{}, handler.EnqueueRequestsFromMapFunc(r.machineDeploymentToCluster), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog)), ) if feature.Gates.Enabled(feature.MachinePool) { b = b.Watches( &clusterv1.MachinePool{}, handler.EnqueueRequestsFromMapFunc(r.machinePoolToCluster), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog)), ) } @@ -180,7 +177,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (retRes ct defer func() { // Always reconcile the Status. if err := r.updateStatus(ctx, s); err != nil { - retRes = ctrl.Result{} reterr = kerrors.NewAggregate([]error{reterr, err}) return } @@ -194,10 +190,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (retRes ct if err := patchCluster(ctx, patchHelper, cluster, patchOpts...); err != nil { reterr = kerrors.NewAggregate([]error{reterr, err}) } - - if reterr != nil { - retRes = ctrl.Result{} - } }() alwaysReconcile := []clusterReconcileFunc{ @@ -631,7 +623,7 @@ func (c *clusterDescendants) filterOwnedDescendants(cluster *clusterv1.Cluster) return nil //nolint:nilerr // We don't want to exit the EachListItem loop, just continue } - if util.IsOwnedByObject(acc, cluster) { + if util.IsOwnedByObject(acc, cluster, clusterv1.GroupVersion.WithKind("Cluster").GroupKind()) { ownedDescendants = append(ownedDescendants, obj) } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/cluster/cluster_controller_phases.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/cluster/cluster_controller_phases.go index 531c47001f..d63670755f 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/cluster/cluster_controller_phases.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/cluster/cluster_controller_phases.go @@ -361,7 +361,7 @@ func (r *Reconciler) reconcileKubeconfig(ctx context.Context, s *scope) (ctrl.Re switch { case apierrors.IsNotFound(err): if err := kubeconfig.CreateSecret(ctx, r.Client, cluster); err != nil { - if err == kubeconfig.ErrDependentCertificateNotFound { + if errors.Is(err, kubeconfig.ErrDependentCertificateNotFound) { log.Info("Could not find secret for cluster, requeuing", "Secret", secret.ClusterCA) return ctrl.Result{RequeueAfter: 30 * time.Second}, nil } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/cluster/cluster_controller_status.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/cluster/cluster_controller_status.go index f880ffe0b5..f50caeb981 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/cluster/cluster_controller_status.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/cluster/cluster_controller_status.go @@ -246,7 +246,7 @@ func setWorkersReplicas(_ context.Context, cluster *clusterv1.Cluster, machinePo } for _, ms := range machineSets.Items { - if !util.IsOwnedByObject(&ms, cluster) { + if !util.IsOwnedByObject(&ms, cluster, clusterv1.GroupVersion.WithKind("Cluster").GroupKind()) { continue } if ms.Spec.Replicas != nil { @@ -267,7 +267,7 @@ func setWorkersReplicas(_ context.Context, cluster *clusterv1.Cluster, machinePo } for _, m := range workerMachines.UnsortedList() { - if !util.IsOwnedByObject(m, cluster) { + if !util.IsOwnedByObject(m, cluster, clusterv1.GroupVersion.WithKind("Cluster").GroupKind()) { continue } currentReplicas = ptr.To(ptr.Deref(currentReplicas, 0) + 1) @@ -973,7 +973,7 @@ func setScalingUpCondition(ctx context.Context, cluster *clusterv1.Cluster, cont ws = append(ws, aggregationWrapper{md: &md}) } for _, ms := range machineSets.Items { - if !util.IsOwnedByObject(&ms, cluster) { + if !util.IsOwnedByObject(&ms, cluster, clusterv1.GroupVersion.WithKind("Cluster").GroupKind()) { continue } ws = append(ws, aggregationWrapper{ms: &ms}) @@ -1050,7 +1050,7 @@ func setScalingDownCondition(ctx context.Context, cluster *clusterv1.Cluster, co ws = append(ws, aggregationWrapper{md: &md}) } for _, ms := range machineSets.Items { - if !util.IsOwnedByObject(&ms, cluster) { + if !util.IsOwnedByObject(&ms, cluster, clusterv1.GroupVersion.WithKind("Cluster").GroupKind()) { continue } ws = append(ws, aggregationWrapper{ms: &ms}) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/clusterclass/clusterclass_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/clusterclass/clusterclass_controller.go index 2d6d1780cc..828e8a40a1 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/clusterclass/clusterclass_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/clusterclass/clusterclass_controller.go @@ -35,7 +35,6 @@ import ( "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -52,6 +51,7 @@ import ( "sigs.k8s.io/cluster-api/internal/contract" internalruntimeclient "sigs.k8s.io/cluster-api/internal/runtime/client" "sigs.k8s.io/cluster-api/internal/topology/variables" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/cache" "sigs.k8s.io/cluster-api/util/patch" @@ -87,13 +87,12 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt } predicateLog := ctrl.LoggerFrom(ctx).WithValues("controller", "clusterclass") - err := ctrl.NewControllerManagedBy(mgr). + err := capicontrollerutil.NewControllerManagedBy(mgr, predicateLog). For(&clusterv1.ClusterClass{}). WithOptions(options). Watches( &runtimev1.ExtensionConfig{}, handler.EnqueueRequestsFromMapFunc(r.extensionConfigToClusterClass), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog)), ). WithEventFilter(predicates.ResourceHasFilterLabel(mgr.GetScheme(), predicateLog, r.WatchFilterValue)). Complete(r) @@ -156,10 +155,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (retres ct reterr = kerrors.NewAggregate([]error{reterr, err}) return } - - if reterr != nil { - retres = ctrl.Result{} - } }() reconcileNormal := []clusterClassReconcileFunc{ diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/clusterresourceset/clusterresourceset_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/clusterresourceset/clusterresourceset_controller.go index 1d1bf0ed75..7056bc5805 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/clusterresourceset/clusterresourceset_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/clusterresourceset/clusterresourceset_controller.go @@ -32,7 +32,6 @@ import ( "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" @@ -45,6 +44,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/controllers/clustercache" resourcepredicates "sigs.k8s.io/cluster-api/internal/controllers/clusterresourceset/predicates" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/conditions" v1beta1conditions "sigs.k8s.io/cluster-api/util/conditions/deprecated/v1beta1" @@ -77,12 +77,11 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt } predicateLog := ctrl.LoggerFrom(ctx).WithValues("controller", "clusterresourceset") - err := ctrl.NewControllerManagedBy(mgr). + err := capicontrollerutil.NewControllerManagedBy(mgr, predicateLog). For(&addonsv1.ClusterResourceSet{}). Watches( &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(r.clusterToClusterResourceSet), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog)), ). WatchesRawSource(r.ClusterCache.GetClusterSource("clusterresourceset", r.clusterToClusterResourceSet)). WatchesMetadata( @@ -90,12 +89,7 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt handler.EnqueueRequestsFromMapFunc( resourceToClusterResourceSetFunc[client.Object](r.Client), ), - builder.WithPredicates( - predicates.All(mgr.GetScheme(), predicateLog, - predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog), - resourcepredicates.TypedResourceCreateOrUpdate[client.Object](predicateLog), - ), - ), + resourcepredicates.TypedResourceCreateOrUpdate[client.Object](predicateLog), ). WatchesRawSource(source.Kind( partialSecretCache, @@ -314,7 +308,7 @@ func (r *Reconciler) ApplyClusterResourceSet(ctx context.Context, cluster *clust for i, resource := range clusterResourceSet.Spec.Resources { unstructuredObj, err := r.getResource(ctx, resource, cluster.GetNamespace()) if err != nil { - if err == ErrSecretTypeNotSupported { + if errors.Is(err, ErrSecretTypeNotSupported) { v1beta1conditions.MarkFalse(clusterResourceSet, addonsv1.ResourcesAppliedV1Beta1Condition, addonsv1.WrongSecretTypeV1Beta1Reason, clusterv1.ConditionSeverityWarning, "%s", err.Error()) conditions.Set(clusterResourceSet, metav1.Condition{ Type: addonsv1.ClusterResourceSetResourcesAppliedCondition, diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/clusterresourcesetbinding/clusterresourcesetbinding_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/clusterresourcesetbinding/clusterresourcesetbinding_controller.go index c00bd68e50..62e6da77fa 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/clusterresourcesetbinding/clusterresourcesetbinding_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/clusterresourcesetbinding/clusterresourcesetbinding_controller.go @@ -24,7 +24,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -34,6 +33,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/hooks" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/cluster-api/util/predicates" @@ -55,12 +55,11 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt } predicateLog := ctrl.LoggerFrom(ctx).WithValues("controller", "clusterresourcesetbinding") - err := ctrl.NewControllerManagedBy(mgr). + err := capicontrollerutil.NewControllerManagedBy(mgr, predicateLog). For(&addonsv1.ClusterResourceSetBinding{}). Watches( &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(r.clusterToClusterResourceSetBinding), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog)), ). WithOptions(options). WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(mgr.GetScheme(), predicateLog, r.WatchFilterValue)). diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/controllers/doc.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/extensionconfig/doc.go similarity index 85% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/controllers/doc.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/extensionconfig/doc.go index bc5c76004d..60b134aba9 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/controllers/doc.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/extensionconfig/doc.go @@ -14,5 +14,5 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package controllers implements the exp/runtime controllers. -package controllers +// Package extensionconfig implements the ExtensionConfig controller. +package extensionconfig diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/extensionconfig_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/extensionconfig/extensionconfig_controller.go similarity index 63% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/extensionconfig_controller.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/extensionconfig/extensionconfig_controller.go index d85730c41c..312c72df72 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/extensionconfig_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/extensionconfig/extensionconfig_controller.go @@ -14,12 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package extensionconfig import ( + "bytes" "context" "fmt" "strings" + "time" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" @@ -39,6 +41,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" runtimev1 "sigs.k8s.io/cluster-api/api/runtime/v1beta2" runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/util/conditions" v1beta1conditions "sigs.k8s.io/cluster-api/util/conditions/deprecated/v1beta1" "sigs.k8s.io/cluster-api/util/patch" @@ -56,23 +59,39 @@ const ( // Reconciler reconciles an ExtensionConfig object. type Reconciler struct { - Client client.Client - APIReader client.Reader - RuntimeClient runtimeclient.Client + Client client.Client + APIReader client.Reader + RuntimeClient runtimeclient.Client + PartialSecretCache cache.Cache + + // ReadOnly configures if the ExtensionConfig controller should write ExtensionConfig objects or only read them + ReadOnly bool + // WatchFilterValue is the label value used to filter events prior to reconciliation. WatchFilterValue string } -func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options, partialSecretCache cache.Cache) error { +func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { if r.Client == nil || r.APIReader == nil || r.RuntimeClient == nil { return errors.New("Client, APIReader and RuntimeClient must not be nil") } + if r.ReadOnly && r.PartialSecretCache != nil { + return errors.New("PartialSecretCache must not be set if ReadOnly is true") + } + if !r.ReadOnly && r.PartialSecretCache == nil { + return errors.New("PartialSecretCache must be set if ReadOnly is false") + } predicateLog := ctrl.LoggerFrom(ctx).WithValues("controller", "extensionconfig") - err := ctrl.NewControllerManagedBy(mgr). + b := capicontrollerutil.NewControllerManagedBy(mgr, predicateLog). For(&runtimev1.ExtensionConfig{}). - WatchesRawSource(source.Kind( - partialSecretCache, + WithOptions(options). + WithEventFilter(predicates.ResourceHasFilterLabel(mgr.GetScheme(), predicateLog, r.WatchFilterValue)) + + if !r.ReadOnly { + // The watch on Secrets is only needed when reconciling caBundle (readOnly mode doesn't do that). + b.WatchesRawSource(source.Kind( + r.PartialSecretCache, &metav1.PartialObjectMetadata{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", @@ -83,11 +102,10 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt r.secretToExtensionConfig, ), predicates.TypedResourceIsChanged[*metav1.PartialObjectMetadata](mgr.GetScheme(), predicateLog), - )). - WithOptions(options). - WithEventFilter(predicates.ResourceHasFilterLabel(mgr.GetScheme(), predicateLog, r.WatchFilterValue)). - Complete(r) - if err != nil { + )) + } + + if err := b.Complete(r); err != nil { return errors.Wrap(err, "failed setting up with a controller manager") } @@ -97,10 +115,11 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt // warmupRunnable will attempt to sync the RuntimeSDK registry with existing ExtensionConfig objects to ensure extensions // are discovered before controllers begin reconciling. - err = mgr.Add(&warmupRunnable{ + err := mgr.Add(&warmupRunnable{ Client: r.Client, APIReader: r.APIReader, RuntimeClient: r.RuntimeClient, + ReadOnly: r.ReadOnly, }) if err != nil { return errors.Wrap(err, "failed adding warmupRunnable to controller manager") @@ -109,13 +128,12 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt } func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - var errs []error log := ctrl.LoggerFrom(ctx) // Requeue events when the registry is not ready. // The registry will become ready after it is 'warmed up' by warmupRunnable. if !r.RuntimeClient.IsReady() { - return ctrl.Result{Requeue: true}, nil + return ctrl.Result{RequeueAfter: 10 * time.Second}, nil } extensionConfig := &runtimev1.ExtensionConfig{} @@ -132,43 +150,47 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu return ctrl.Result{}, err } - // Copy to avoid modifying the original extensionConfig. - original := extensionConfig.DeepCopy() - - if isPaused, requeue, err := paused.EnsurePausedCondition(ctx, r.Client, nil, extensionConfig); err != nil || isPaused || requeue { - return ctrl.Result{}, err - } - // Handle deletion reconciliation loop. + // Note: This only unregisters the ExtensionConfig which is intentionally done even if the ExtensionConfig is paused. if !extensionConfig.DeletionTimestamp.IsZero() { return r.reconcileDelete(ctx, extensionConfig) } - // Inject CABundle from secret if annotation is set. Otherwise https calls may fail. - if err := reconcileCABundle(ctx, r.Client, extensionConfig); err != nil { - return ctrl.Result{}, err - } + // In readOnly mode only validate instead of reconciling CA bundle and running discovery. + if r.ReadOnly { + if conditions.IsTrue(extensionConfig, clusterv1.PausedCondition) { + return ctrl.Result{}, nil + } - // discoverExtensionConfig will return a discovered ExtensionConfig with the appropriate conditions. - discoveredExtensionConfig, err := discoverExtensionConfig(ctx, r.RuntimeClient, extensionConfig) - if err != nil { - errs = append(errs, err) - } + if err := validateExtensionConfig(extensionConfig); err != nil { + return ctrl.Result{}, errors.Wrapf(err, "failed to validate ExtensionConfig") + } - // Always patch the ExtensionConfig as it may contain updates in conditions or clientConfig.caBundle. - if err = patchExtensionConfig(ctx, r.Client, original, discoveredExtensionConfig); err != nil { - errs = append(errs, err) - } + // Register the ExtensionConfig if it is valid. + log.V(4).Info("Registering ExtensionConfig information into registry") + if err = r.RuntimeClient.Register(extensionConfig); err != nil { + return ctrl.Result{}, errors.Wrapf(err, "failed to register ExtensionConfig %s/%s", extensionConfig.Namespace, extensionConfig.Name) + } + } else { + // Preserve original, EnsurePausedCondition might bump observedGeneration of the Paused condition without requeuing. + original := extensionConfig.DeepCopy() - if len(errs) != 0 { - return ctrl.Result{}, kerrors.NewAggregate(errs) - } + if isPaused, requeue, err := paused.EnsurePausedCondition(ctx, r.Client, nil, extensionConfig); err != nil || isPaused || requeue { + return ctrl.Result{}, err + } - // Register the ExtensionConfig if it was found and patched without error. - log.V(4).Info("Registering ExtensionConfig information into registry") - if err = r.RuntimeClient.Register(discoveredExtensionConfig); err != nil { - return ctrl.Result{}, errors.Wrapf(err, "failed to register ExtensionConfig %s/%s", extensionConfig.Namespace, extensionConfig.Name) + extensionConfig, err := reconcileExtensionConfig(ctx, r.Client, r.RuntimeClient, original, extensionConfig) + if err != nil { + return ctrl.Result{}, errors.Wrapf(err, "failed to reconcile ExtensionConfig") + } + + // Register the ExtensionConfig if it was found and patched without error. + log.V(4).Info("Registering ExtensionConfig information into registry") + if err = r.RuntimeClient.Register(extensionConfig); err != nil { + return ctrl.Result{}, errors.Wrapf(err, "failed to register ExtensionConfig %s/%s", extensionConfig.Namespace, extensionConfig.Name) + } } + return ctrl.Result{}, nil } @@ -292,3 +314,61 @@ func splitNamespacedName(nameStr string) types.NamespacedName { } return types.NamespacedName{Namespace: nameStr[:splitPoint], Name: nameStr[splitPoint+1:]} } + +func validateExtensionConfig(extensionConfig *runtimev1.ExtensionConfig) error { + // Verify caBundle (a more complete validation would be too much effort here) + if len(extensionConfig.Spec.ClientConfig.CABundle) == 0 { + return errors.Errorf("caBundle is not set on ExtensionConfig %s", klog.KObj(extensionConfig)) + } + + // Verify discovery. + discoveredCondition := conditions.Get(extensionConfig, runtimev1.ExtensionConfigDiscoveredCondition) + switch { + case discoveredCondition == nil: + return errors.Errorf("%s condition not yet set on ExtensionConfig %s", runtimev1.ExtensionConfigDiscoveredCondition, klog.KObj(extensionConfig)) + case discoveredCondition.Status != metav1.ConditionTrue: + return errors.Errorf("%s condition on ExtensionConfig %s must have status: True (instead it has: %s)", runtimev1.ExtensionConfigDiscoveredCondition, klog.KObj(extensionConfig), discoveredCondition.Status) + case discoveredCondition.ObservedGeneration != extensionConfig.Generation: + return errors.Errorf("%s condition on ExtensionConfig %s must have observedGeneration: %d (instead it has: %d)", runtimev1.ExtensionConfigDiscoveredCondition, klog.KObj(extensionConfig), extensionConfig.Generation, discoveredCondition.ObservedGeneration) + } + + return nil +} + +func reconcileExtensionConfig(ctx context.Context, c client.Client, runtimeClient runtimeclient.Client, original, extensionConfig *runtimev1.ExtensionConfig) (*runtimev1.ExtensionConfig, error) { + // Inject CABundle from secret if annotation is set. Otherwise https calls may fail. + if err := reconcileCABundle(ctx, c, extensionConfig); err != nil { + return nil, err + } + if !bytes.Equal(original.Spec.ClientConfig.CABundle, extensionConfig.Spec.ClientConfig.CABundle) { + // Note: This is intentionally not using the patch helper as the patch helper does not propagate metadata.generation back. + // We want to have the current generation here because otherwise the condition set below would have an outdated observedGeneration. + if err := c.Patch(ctx, extensionConfig, client.MergeFrom(original)); err != nil { + return nil, errors.Wrapf(err, "failed to patch ExtensionConfig %s", klog.KObj(extensionConfig)) + } + // Update original so that patchExtensionConfig below does not try to patch caBundle again. + // Note: This means that we might lose observedGeneration bumps on the Paused condition, but: + // * in this code path the generation changed again anyway (so the bump would have been outdated) + // * the next reconcile that doesn't change caBundle will bump the observedGeneration of the Paused + // condition in patchExtensionConfig + original = extensionConfig.DeepCopy() + } + + var errs []error + // discoverExtensionConfig will return a discovered ExtensionConfig with the appropriate conditions. + extensionConfig, err := discoverExtensionConfig(ctx, runtimeClient, extensionConfig) + if err != nil { + errs = append(errs, err) + } + + // Note: Intentionally always patching ExtensionConfig even if discoverExtensionConfig failed. + if err := patchExtensionConfig(ctx, c, original, extensionConfig); err != nil { + errs = append(errs, err) + } + + if len(errs) > 0 { + return nil, kerrors.NewAggregate(errs) + } + + return extensionConfig, nil +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/index.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/extensionconfig/index.go similarity index 98% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/index.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/extensionconfig/index.go index 34598daeb7..b4e48f4a8d 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/index.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/extensionconfig/index.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package extensionconfig import ( "context" diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/warmup.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/extensionconfig/warmup.go similarity index 71% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/warmup.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/extensionconfig/warmup.go index a7d090e135..13a79087af 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/runtime/internal/controllers/warmup.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/extensionconfig/warmup.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package extensionconfig import ( "context" @@ -44,6 +44,7 @@ type warmupRunnable struct { Client client.Client APIReader client.Reader RuntimeClient runtimeclient.Client + ReadOnly bool warmupTimeout time.Duration warmupInterval time.Duration } @@ -72,16 +73,16 @@ func (r *warmupRunnable) Start(ctx context.Context) error { ctx, cancel := context.WithTimeoutCause(ctx, r.warmupTimeout, errors.New("warmup timeout expired")) defer cancel() + var warmupErr error err := wait.PollUntilContextTimeout(ctx, r.warmupInterval, r.warmupTimeout, true, func(ctx context.Context) (done bool, err error) { - if err = warmupRegistry(ctx, r.Client, r.APIReader, r.RuntimeClient); err != nil { - log.Error(err, "ExtensionConfig registry warmup failed") - return false, nil + if warmupErr = r.warmupRegistry(ctx); warmupErr != nil { + log.Error(warmupErr, "ExtensionConfig registry warmup failed") + return false, nil //nolint:nilerr // Intentionally not returning the error here } return true, nil }) - if err != nil { - return errors.Wrapf(err, "ExtensionConfig registry warmup timed out after %s", r.warmupTimeout.String()) + return errors.Wrapf(warmupErr, "ExtensionConfig registry warmup timed out after %s", r.warmupTimeout.String()) } return nil @@ -89,48 +90,44 @@ func (r *warmupRunnable) Start(ctx context.Context) error { // warmupRegistry attempts to discover all existing ExtensionConfigs and patch their status with discovered Handlers. // It warms up the registry by passing it the up-to-date list of ExtensionConfigs. -func warmupRegistry(ctx context.Context, client client.Client, reader client.Reader, runtimeClient runtimeclient.Client) error { +func (r *warmupRunnable) warmupRegistry(ctx context.Context) error { log := ctrl.LoggerFrom(ctx) - var errs []error - extensionConfigList := runtimev1.ExtensionConfigList{} - if err := reader.List(ctx, &extensionConfigList); err != nil { + if err := r.APIReader.List(ctx, &extensionConfigList); err != nil { return errors.Wrapf(err, "failed to list ExtensionConfigs") } + var errs []error for i := range extensionConfigList.Items { extensionConfig := &extensionConfigList.Items[i] - original := extensionConfig.DeepCopy() log := log.WithValues("ExtensionConfig", klog.KObj(extensionConfig)) ctx := ctrl.LoggerInto(ctx, log) - // Inject CABundle from secret if annotation is set. Otherwise https calls may fail. - if err := reconcileCABundle(ctx, client, extensionConfig); err != nil { - errs = append(errs, err) - // Note: we continue here because if reconcileCABundle doesn't work discovery will fail as well. - continue - } - - extensionConfig, err := discoverExtensionConfig(ctx, runtimeClient, extensionConfig) - if err != nil { - errs = append(errs, err) - } - - // Always patch the ExtensionConfig as it may contain updates in conditions or clientConfig.caBundle. - if err = patchExtensionConfig(ctx, client, original, extensionConfig); err != nil { - errs = append(errs, err) + // In readOnly mode only validate instead of reconciling CA bundle and running discovery. + if r.ReadOnly { + if err := validateExtensionConfig(extensionConfig); err != nil { + errs = append(errs, errors.Wrapf(err, "failed to validate ExtensionConfig")) + } + } else { + // extensionConfig is equal to original here, but we have to deepcopy so that if extensionConfig is changed original is not changed. + original := extensionConfig.DeepCopy() + extensionConfig, err := reconcileExtensionConfig(ctx, r.Client, r.RuntimeClient, original, extensionConfig) + if err != nil { + errs = append(errs, errors.Wrapf(err, "failed to reconcile ExtensionConfig")) + continue + } + extensionConfigList.Items[i] = *extensionConfig } - extensionConfigList.Items[i] = *extensionConfig } - // If there was some error in discovery or patching return before committing to the Registry. - if len(errs) != 0 { + // If there was an error in discovery or patching return before committing to the registry. + if len(errs) > 0 { return kerrors.NewAggregate(errs) } - if err := runtimeClient.WarmUp(&extensionConfigList); err != nil { + if err := r.RuntimeClient.WarmUp(&extensionConfigList); err != nil { return err } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/drain/drain.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/drain/drain.go index 30511c0bd7..3232fe288d 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/drain/drain.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/drain/drain.go @@ -375,7 +375,7 @@ evictionLoop: // Ensure the causes are also included in the error message. // Before: "Cannot evict pod as it would violate the pod's disruption budget." // After: "Cannot evict pod as it would violate the pod's disruption budget. The disruption budget nginx needs 20 healthy pods and has 20 currently" - if ok := errors.As(err, &statusError); ok { + if errors.As(err, &statusError) { errorMessage := statusError.Status().Message if statusError.Status().Details != nil { var causes []string diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller.go index 428ed8195f..c572d0628b 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller.go @@ -39,7 +39,6 @@ import ( "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -52,9 +51,11 @@ import ( "sigs.k8s.io/cluster-api/controllers/clustercache" "sigs.k8s.io/cluster-api/controllers/external" "sigs.k8s.io/cluster-api/controllers/noderefutil" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/contract" "sigs.k8s.io/cluster-api/internal/controllers/machine/drain" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/cache" @@ -93,9 +94,10 @@ var ( // Reconciler reconciles a Machine object. type Reconciler struct { - Client client.Client - APIReader client.Reader - ClusterCache clustercache.ClusterCache + Client client.Client + APIReader client.Reader + ClusterCache clustercache.ClusterCache + RuntimeClient runtimeclient.Client // WatchFilterValue is the label value used to filter events prior to reconciliation. WatchFilterValue string @@ -105,7 +107,7 @@ type Reconciler struct { AdditionalSyncMachineLabels []*regexp.Regexp AdditionalSyncMachineAnnotations []*regexp.Regexp - controller controller.Controller + controller capicontrollerutil.Controller recorder record.EventRecorder externalTracker external.ObjectTracker @@ -113,10 +115,7 @@ type Reconciler struct { // during a single reconciliation. nodeDeletionRetryTimeout time.Duration - // reconcileDeleteCache is used to store when reconcileDelete should not be executed before a - // specific time for a specific Request. This is used to implement rate-limiting to avoid - // e.g. spamming workload clusters with eviction requests during Node drain. - reconcileDeleteCache cache.Cache[cache.ReconcileEntry] + hookCache cache.Cache[cache.HookEntry] predicateLog *logr.Logger } @@ -129,6 +128,9 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt // to have some buffer. return errors.New("Client, APIReader and ClusterCache must not be nil and RemoteConditionsGracePeriod must not be < 2m") } + if feature.Gates.Enabled(feature.InPlaceUpdates) && r.RuntimeClient == nil { + return errors.New("RuntimeClient must not be nil when InPlaceUpdates feature gate is enabled") + } r.predicateLog = ptr.To(ctrl.LoggerFrom(ctx).WithValues("controller", "machine")) clusterToMachines, err := util.ClusterToTypedObjectsMapper(mgr.GetClient(), &clusterv1.MachineList{}, mgr.GetScheme()) @@ -147,37 +149,33 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt if r.nodeDeletionRetryTimeout.Nanoseconds() == 0 { r.nodeDeletionRetryTimeout = 10 * time.Second } - c, err := ctrl.NewControllerManagedBy(mgr). + + c, err := capicontrollerutil.NewControllerManagedBy(mgr, *r.predicateLog). For(&clusterv1.Machine{}). WithOptions(options). WithEventFilter(predicates.ResourceHasFilterLabel(mgr.GetScheme(), *r.predicateLog, r.WatchFilterValue)). Watches( &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(clusterToMachines), - builder.WithPredicates( - // TODO: should this wait for Cluster.Status.InfrastructureReady similar to Infra Machine resources? - predicates.All(mgr.GetScheme(), *r.predicateLog, - predicates.ResourceIsChanged(mgr.GetScheme(), *r.predicateLog), - predicates.ClusterControlPlaneInitialized(mgr.GetScheme(), *r.predicateLog), - predicates.ResourceHasFilterLabel(mgr.GetScheme(), *r.predicateLog, r.WatchFilterValue), - ), - )). + // TODO: should this wait for Cluster.Status.InfrastructureReady similar to Infra Machine resources? + predicates.ClusterControlPlaneInitialized(mgr.GetScheme(), *r.predicateLog), + predicates.ResourceHasFilterLabel(mgr.GetScheme(), *r.predicateLog, r.WatchFilterValue), + ). WatchesRawSource(r.ClusterCache.GetClusterSource("machine", clusterToMachines, clustercache.WatchForProbeFailure(r.RemoteConditionsGracePeriod))). Watches( &clusterv1.MachineSet{}, handler.EnqueueRequestsFromMapFunc(msToMachines), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), *r.predicateLog)), ). Watches( &clusterv1.MachineDeployment{}, handler.EnqueueRequestsFromMapFunc(mdToMachines), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), *r.predicateLog)), ). Build(r) if err != nil { return errors.Wrap(err, "failed setting up with a controller manager") } + r.hookCache = cache.New[cache.HookEntry](cache.HookCacheDefaultTTL) r.controller = c r.recorder = mgr.GetEventRecorderFor("machine-controller") r.externalTracker = external.ObjectTracker{ @@ -186,11 +184,10 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt Scheme: mgr.GetScheme(), PredicateLogger: r.predicateLog, } - r.reconcileDeleteCache = cache.New[cache.ReconcileEntry](cache.DefaultTTL) return nil } -func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) { +func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (retres ctrl.Result, reterr error) { // Fetch the Machine instance m := &clusterv1.Machine{} if err := r.Client.Get(ctx, req.NamespacedName, m); err != nil { @@ -220,8 +217,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re cluster, err := util.GetClusterByName(ctx, r.Client, m.Namespace, m.Spec.ClusterName) if err != nil { - return ctrl.Result{}, errors.Wrapf(err, "failed to get cluster %q for machine %q in namespace %q", - m.Spec.ClusterName, m.Name, m.Namespace) + return ctrl.Result{}, err } // Initialize the patch helper @@ -234,23 +230,26 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re return ctrl.Result{}, err } - if !m.DeletionTimestamp.IsZero() { - // Check reconcileDeleteCache to ensure we won't run reconcileDelete too frequently. - // Note: The reconcileDelete func will add entries to the cache. - if cacheEntry, ok := r.reconcileDeleteCache.Has(cache.NewReconcileEntryKey(m)); ok { - if requeueAfter, requeue := cacheEntry.ShouldRequeue(time.Now()); requeue { - return ctrl.Result{RequeueAfter: requeueAfter}, nil - } - } - } - s := &scope{ cluster: cluster, machine: m, } + // If the Machine is controlled by a MachineSet, read it. + s.owningMachineSet, err = r.getOwnerMachineSet(ctx, s.machine) + if err != nil { + return ctrl.Result{}, err + } + + // If the MachineSet is controlled by a MachineDeployment, get it as well. + s.owningMachineDeployment, err = r.getOwnerMachineDeployment(ctx, s.owningMachineSet) + if err != nil { + return ctrl.Result{}, err + } + defer func() { - r.updateStatus(ctx, s) + updateRes := r.updateStatus(ctx, s) + retres = util.LowestNonZeroResult(retres, updateRes) // Always attempt to patch the object and status after each reconciliation. // Patch ObservedGeneration only if the reconciliation completed successfully @@ -282,7 +281,41 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re } // Handle normal reconciliation loop. - return doReconcile(ctx, alwaysReconcile, s) + reconcileNormal := append( + alwaysReconcile, + r.reconcileInPlaceUpdate, + ) + + return doReconcile(ctx, reconcileNormal, s) +} + +func (r *Reconciler) getOwnerMachineSet(ctx context.Context, m *clusterv1.Machine) (*clusterv1.MachineSet, error) { + machineSetKey, notFound, err := getOwnerMachineSetObjectKey(m.ObjectMeta) + if err != nil || notFound || machineSetKey == nil { + return nil, err + } + ms := &clusterv1.MachineSet{} + if err := r.Client.Get(ctx, *machineSetKey, ms); err != nil { + return nil, fmt.Errorf("failed to retrieve owner MachineSet for Machine %s: %w", klog.KObj(m), err) + } + return ms, nil +} + +func (r *Reconciler) getOwnerMachineDeployment(ctx context.Context, ms *clusterv1.MachineSet) (*clusterv1.MachineDeployment, error) { + if ms == nil { + return nil, nil + } + + mdName := ms.Labels[clusterv1.MachineDeploymentNameLabel] + if mdName == "" { + return nil, nil + } + + md := &clusterv1.MachineDeployment{} + if err := r.Client.Get(ctx, client.ObjectKey{Namespace: ms.Namespace, Name: mdName}, md); err != nil { + return nil, fmt.Errorf("failed to retrieve owner MachineDeployment for MachineSet %s: %w", klog.KObj(ms), err) + } + return md, nil } func patchMachine(ctx context.Context, patchHelper *patch.Helper, machine *clusterv1.Machine, options ...patch.Option) error { @@ -326,6 +359,7 @@ func patchMachine(ctx context.Context, patchHelper *patch.Helper, machine *clust clusterv1.MachineNodeReadyCondition, clusterv1.MachineNodeHealthyCondition, clusterv1.MachineDeletingCondition, + clusterv1.MachineUpdatingCondition, }}, ) @@ -366,6 +400,14 @@ type scope struct { // of the reconcile function. machine *clusterv1.Machine + // owningMachineSet return the MachineSet owning this machine. + // Note: The value will be nil in case of stand-alone machines. + owningMachineSet *clusterv1.MachineSet + + // owningMachineDeployment return the MachineDeployment controlling the MachineSet owning this machine. + // Note: The value will be nil in case of stand-alone Machines or in case of stand-alone MachineSet. + owningMachineDeployment *clusterv1.MachineDeployment + // infraMachine is the Infrastructure Machine object that is referenced by the // Machine. It is set after reconcileInfrastructure is called. infraMachine *unstructured.Unstructured @@ -386,17 +428,17 @@ type scope struct { // nodeGetError is the error that occurred when trying to get the Node. nodeGetError error - // reconcileDeleteExecuted will be set to true if the logic in reconcileDelete is executed. - // We might requeue early in reconcileDelete because of rate-limiting. - // If the Machine has the deletionTimestamp set and this field is false we don't update the - // Deleting condition. - reconcileDeleteExecuted bool - // deletingReason is the reason that should be used when setting the Deleting condition. deletingReason string // deletingMessage is the message that should be used when setting the Deleting condition. deletingMessage string + + // updatingReason is the reason that should be used when setting the Updating condition. + updatingReason string + + // updatingMessage is the message that should be used when setting the Updating condition. + updatingMessage string } func (r *Reconciler) reconcileMachineOwnerAndLabels(_ context.Context, s *scope) (ctrl.Result, error) { @@ -425,14 +467,6 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, s *scope) (ctrl.Result cluster := s.cluster m := s.machine - s.reconcileDeleteExecuted = true - - // Add entry to the reconcileDeleteCache so we won't run reconcileDelete more than once per second. - // Under certain circumstances the ReconcileAfter time will be set to a later time, e.g. when we're waiting - // for Pods to terminate or volumes to detach. - // This is done to ensure we're not spamming the workload cluster API server. - r.reconcileDeleteCache.Add(cache.NewReconcileEntry(s.machine, time.Now().Add(1*time.Second))) - // Set "fallback" reason and message. This is used if we don't set a more specific reason and message below. s.deletingReason = clusterv1.MachineDeletingReason s.deletingMessage = "Deletion started" @@ -601,7 +635,7 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, s *scope) (ctrl.Result // We only delete the node after the underlying infrastructure is gone. // https://github.com/kubernetes-sigs/cluster-api/issues/2565 if isDeleteNodeAllowed { - log.Info("Deleting node", "Node", klog.KRef("", m.Status.NodeRef.Name)) + log.Info("Deleting Node", "Node", klog.KRef("", m.Status.NodeRef.Name)) var deleteNodeErr error waitErr := wait.PollUntilContextTimeout(ctx, 2*time.Second, r.nodeDeletionRetryTimeout, true, func(ctx context.Context) (bool, error) { @@ -611,9 +645,9 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, s *scope) (ctrl.Result return true, nil }) if waitErr != nil { - log.Error(deleteNodeErr, "Timed out deleting node", "Node", klog.KRef("", m.Status.NodeRef.Name)) + log.Error(deleteNodeErr, "Timed out deleting Node", "Node", klog.KRef("", m.Status.NodeRef.Name)) v1beta1conditions.MarkFalse(m, clusterv1.MachineNodeHealthyV1Beta1Condition, clusterv1.DeletionFailedV1Beta1Reason, clusterv1.ConditionSeverityWarning, "") - r.recorder.Eventf(m, corev1.EventTypeWarning, "FailedDeleteNode", "error deleting Machine's node: %v", deleteNodeErr) + r.recorder.Eventf(m, corev1.EventTypeWarning, "FailedDeleteNode", "error deleting Machine's Node: %v", deleteNodeErr) // If the node deletion timeout is not expired yet, requeue the Machine for reconciliation. if m.Spec.Deletion.NodeDeletionTimeoutSeconds == nil || *m.Spec.Deletion.NodeDeletionTimeoutSeconds == 0 || m.DeletionTimestamp.Add(time.Duration(*m.Spec.Deletion.NodeDeletionTimeoutSeconds)*time.Second).After(time.Now()) { @@ -881,8 +915,8 @@ func (r *Reconciler) drainNode(ctx context.Context, s *scope) (ctrl.Result, erro return ctrl.Result{}, nil } - // Add entry to the reconcileDeleteCache so we won't retry drain again before drainRetryInterval. - r.reconcileDeleteCache.Add(cache.NewReconcileEntry(machine, time.Now().Add(drainRetryInterval))) + // Slow down the reconcile frequency, because Node drain is a slow process. + r.controller.DeferNextReconcileForObject(machine, time.Now().Add(drainRetryInterval)) conditionMessage := evictionResult.ConditionMessage(machine.Status.Deletion.NodeDrainStartTime) v1beta1conditions.MarkFalse(machine, clusterv1.DrainingSucceededV1Beta1Condition, clusterv1.DrainingV1Beta1Reason, clusterv1.ConditionSeverityInfo, "%s", conditionMessage) @@ -967,8 +1001,8 @@ func (r *Reconciler) shouldWaitForNodeVolumes(ctx context.Context, s *scope) (ct return ctrl.Result{}, nil } - // Add entry to the reconcileDeleteCache so we won't retry shouldWaitForNodeVolumes again before waitForVolumeDetachRetryInterval. - r.reconcileDeleteCache.Add(cache.NewReconcileEntry(machine, time.Now().Add(waitForVolumeDetachRetryInterval))) + // Slow down the reconcile process, because volume detach is a slow process. + r.controller.DeferNextReconcileForObject(machine, time.Now().Add(waitForVolumeDetachRetryInterval)) s.deletingReason = clusterv1.MachineDeletingWaitingForVolumeDetachReason s.deletingMessage = attachedVolumeInformation.conditionMessage(machine) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_inplace_update.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_inplace_update.go new file mode 100644 index 0000000000..beea663b27 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_inplace_update.go @@ -0,0 +1,284 @@ +/* +Copyright 2025 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 machine + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog/v2" + "k8s.io/utils/ptr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" + "sigs.k8s.io/cluster-api/feature" + "sigs.k8s.io/cluster-api/internal/hooks" + "sigs.k8s.io/cluster-api/util/cache" +) + +// reconcileInPlaceUpdate handles the in-place update workflow for a Machine. +func (r *Reconciler) reconcileInPlaceUpdate(ctx context.Context, s *scope) (ctrl.Result, error) { + if !feature.Gates.Enabled(feature.InPlaceUpdates) { + return ctrl.Result{}, nil + } + + log := ctrl.LoggerFrom(ctx) + + machineAnnotations := s.machine.GetAnnotations() + _, inPlaceUpdateInProgress := machineAnnotations[clusterv1.UpdateInProgressAnnotation] + hasUpdateMachinePending := hooks.IsPending(runtimehooksv1.UpdateMachine, s.machine) + + if !inPlaceUpdateInProgress { + // Clean up any orphaned pending hooks and annotations before exiting. + // This can happen if the in-place update annotation was removed from Machine + // but the UpdateMachine hook is still pending or annotations are still on InfraMachine/BootstrapConfig. + if hasUpdateMachinePending { + log.Info("In-place update annotation removed but UpdateMachine hook still pending, cleaning up orphaned hook and annotations") + if err := r.completeInPlaceUpdate(ctx, s); err != nil { + return ctrl.Result{}, errors.Wrap(err, "failed to clean up orphaned UpdateMachine hook and annotations") + } + } + + return ctrl.Result{}, nil + } + + // If hook is not pending, we're waiting for the owner controller to mark it as pending. + if !hasUpdateMachinePending { + log.Info("Machine marked for in-place update, waiting for owning controller to mark UpdateMachine hook as pending") + return ctrl.Result{}, nil + } + + if !ptr.Deref(s.machine.Status.Initialization.InfrastructureProvisioned, false) { + log.V(5).Info("Infrastructure not yet provisioned, skipping in-place update") + return ctrl.Result{}, nil + } + if !ptr.Deref(s.machine.Status.Initialization.BootstrapDataSecretCreated, false) { + log.V(5).Info("Bootstrap data secret not yet created, skipping in-place update") + return ctrl.Result{}, nil + } + + if !s.machine.Status.NodeRef.IsDefined() { + log.V(5).Info("Machine status.nodeRef is not yet set, skipping in-place update") + return ctrl.Result{}, nil + } + + if s.infraMachine == nil { + s.updatingReason = clusterv1.MachineInPlaceUpdateFailedReason + s.updatingMessage = "In-place update not possible: InfraMachine not found" + return ctrl.Result{}, errors.New("in-place update failed: InfraMachine not found") + } + + infraReady := r.isInfraMachineReadyForUpdate(s) + bootstrapReady := r.isBootstrapConfigReadyForUpdate(s) + + if !infraReady || !bootstrapReady { + log.Info("Waiting for InfraMachine and BootstrapConfig to be marked for in-place update") + return ctrl.Result{}, nil + } + + result, message, err := r.callUpdateMachineHook(ctx, s) + if err != nil { + s.updatingReason = clusterv1.MachineInPlaceUpdateFailedReason + s.updatingMessage = "UpdateMachine hook failed: please check controller logs for errors" + return ctrl.Result{}, errors.Wrap(err, "in-place update failed") + } + + if result.RequeueAfter > 0 { + s.updatingReason = clusterv1.MachineInPlaceUpdatingReason + if message != "" { + s.updatingMessage = fmt.Sprintf("In-place update in progress: %s", message) + } else { + s.updatingMessage = "In-place update in progress" + } + return result, nil + } + + if err := r.completeInPlaceUpdate(ctx, s); err != nil { + return ctrl.Result{}, errors.Wrap(err, "failed to complete in-place update") + } + + return ctrl.Result{}, nil +} + +// isInfraMachineReadyForUpdate checks if the InfraMachine has the in-place update annotation. +func (r *Reconciler) isInfraMachineReadyForUpdate(s *scope) bool { + _, hasAnnotation := s.infraMachine.GetAnnotations()[clusterv1.UpdateInProgressAnnotation] + return hasAnnotation +} + +// isBootstrapConfigReadyForUpdate checks if the BootstrapConfig has the in-place update annotation. +func (r *Reconciler) isBootstrapConfigReadyForUpdate(s *scope) bool { + if s.bootstrapConfig == nil { + return true + } + _, hasAnnotation := s.bootstrapConfig.GetAnnotations()[clusterv1.UpdateInProgressAnnotation] + return hasAnnotation +} + +// callUpdateMachineHook calls the UpdateMachine runtime hook for the machine. +func (r *Reconciler) callUpdateMachineHook(ctx context.Context, s *scope) (ctrl.Result, string, error) { + log := ctrl.LoggerFrom(ctx) + + // Validate that exactly one extension is registered for the UpdateMachine hook. + // For the current iteration, we only support a single extension to ensure safe behavior. + // Support for multiple extensions will be introduced in a future iteration. + extensions, err := r.RuntimeClient.GetAllExtensions(ctx, runtimehooksv1.UpdateMachine, s.machine) + if err != nil { + return ctrl.Result{}, "", err + } + if len(extensions) == 0 { + return ctrl.Result{}, "", errors.New("no extensions registered for UpdateMachine hook") + } + if len(extensions) > 1 { + return ctrl.Result{}, "", errors.Errorf("found multiple UpdateMachine hooks (%s): only one hook is supported", strings.Join(extensions, ",")) + } + + if cacheEntry, ok := r.hookCache.Has(cache.NewHookEntryKey(s.machine, runtimehooksv1.UpdateMachine)); ok { + if requeueAfter, requeue := cacheEntry.ShouldRequeue(time.Now()); requeue { + log.V(5).Info(fmt.Sprintf("Skip calling UpdateMachine hook, retry after %s", requeueAfter)) + return ctrl.Result{RequeueAfter: requeueAfter}, cacheEntry.ResponseMessage, nil + } + } + + // Note: When building request message, dropping status; Runtime extension should treat UpdateMachine + // requests as desired state; it is up to them to compare with current state and perform necessary actions. + request := &runtimehooksv1.UpdateMachineRequest{ + Desired: runtimehooksv1.UpdateMachineRequestObjects{ + Machine: *cleanupMachine(s.machine), + InfrastructureMachine: runtime.RawExtension{Object: cleanupUnstructured(s.infraMachine)}, + }, + } + + if s.bootstrapConfig != nil { + request.Desired.BootstrapConfig = runtime.RawExtension{Object: cleanupUnstructured(s.bootstrapConfig)} + } + + response := &runtimehooksv1.UpdateMachineResponse{} + + if err := r.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.UpdateMachine, s.machine, request, response); err != nil { + return ctrl.Result{}, "", err + } + + if response.GetRetryAfterSeconds() != 0 { + log.Info(fmt.Sprintf("UpdateMachine hook requested retry after %d seconds", response.GetRetryAfterSeconds())) + requeueAfter := time.Duration(response.RetryAfterSeconds) * time.Second + r.hookCache.Add(cache.NewHookEntry(s.machine, runtimehooksv1.UpdateMachine, time.Now().Add(requeueAfter), response.GetMessage())) + return ctrl.Result{RequeueAfter: requeueAfter}, response.GetMessage(), nil + } + + log.Info("UpdateMachine hook completed successfully") + return ctrl.Result{}, response.GetMessage(), nil +} + +// completeInPlaceUpdate removes in-place update annotations from InfraMachine, BootstrapConfig, Machine, +// and then marks the UpdateMachine hook as done (removes it from pending-hooks annotation). +func (r *Reconciler) completeInPlaceUpdate(ctx context.Context, s *scope) error { + log := ctrl.LoggerFrom(ctx) + + if err := r.removeInPlaceUpdateAnnotation(ctx, s.machine); err != nil { + return err + } + + if s.infraMachine == nil { + log.Info("InfraMachine not found during in-place update completion, skipping annotation removal") + } else { + if err := r.removeInPlaceUpdateAnnotation(ctx, s.infraMachine); err != nil { + return err + } + } + + if s.bootstrapConfig != nil { + if err := r.removeInPlaceUpdateAnnotation(ctx, s.bootstrapConfig); err != nil { + return err + } + } + + // Note: This call will not update the resourceVersion on machine, so that the patchHelper in the main + // Reconcile func won't get a conflict. + if err := hooks.MarkAsDone(ctx, r.Client, s.machine, false, runtimehooksv1.UpdateMachine); err != nil { + return err + } + + log.Info("Completed in-place update") + return nil +} + +// removeInPlaceUpdateAnnotation removes the in-place update annotation from an object and patches it immediately. +func (r *Reconciler) removeInPlaceUpdateAnnotation(ctx context.Context, obj client.Object) error { + annotations := obj.GetAnnotations() + if _, exists := annotations[clusterv1.UpdateInProgressAnnotation]; !exists { + return nil + } + + gvk, err := apiutil.GVKForObject(obj, r.Client.Scheme()) + if err != nil { + return errors.Wrapf(err, "failed to remove %s annotation from object %s", clusterv1.UpdateInProgressAnnotation, klog.KObj(obj)) + } + + // Note: DeepCopy object to not modify the passed-in object which can lead to conflict errors later on. + obj = obj.DeepCopyObject().(client.Object) + orig := obj.DeepCopyObject().(client.Object) + delete(annotations, clusterv1.UpdateInProgressAnnotation) + obj.SetAnnotations(annotations) + + if err := r.Client.Patch(ctx, obj, client.MergeFrom(orig)); err != nil { + return errors.Wrapf(err, "failed to remove %s annotation from %s %s", clusterv1.UpdateInProgressAnnotation, gvk.Kind, klog.KObj(obj)) + } + + return nil +} + +func cleanupMachine(machine *clusterv1.Machine) *clusterv1.Machine { + return &clusterv1.Machine{ + // Set GVK because object is later marshalled with json.Marshal when the hook request is sent. + TypeMeta: metav1.TypeMeta{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "Machine", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: machine.Name, + Namespace: machine.Namespace, + Labels: machine.Labels, + Annotations: machine.Annotations, + }, + Spec: *machine.Spec.DeepCopy(), + } +} + +func cleanupUnstructured(u *unstructured.Unstructured) *unstructured.Unstructured { + cleanedUpU := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": u.GetAPIVersion(), + "kind": u.GetKind(), + "spec": u.Object["spec"], + }, + } + cleanedUpU.SetName(u.GetName()) + cleanedUpU.SetNamespace(u.GetNamespace()) + cleanedUpU.SetLabels(u.GetLabels()) + cleanedUpU.SetAnnotations(u.GetAnnotations()) + return cleanedUpU +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_noderef.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_noderef.go index 7d1a62aca4..04683f6d6e 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_noderef.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_noderef.go @@ -24,16 +24,17 @@ import ( "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/api/core/v1beta2/index" + "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil" "sigs.k8s.io/cluster-api/internal/util/taints" "sigs.k8s.io/cluster-api/util" @@ -69,7 +70,6 @@ func (r *Reconciler) reconcileNode(ctx context.Context, s *scope) (ctrl.Result, // Check that the Machine has a valid ProviderID. if machine.Spec.ProviderID == "" { - log.Info("Waiting for infrastructure provider to report spec.providerID", machine.Spec.InfrastructureRef.Kind, klog.KRef(machine.Namespace, machine.Spec.InfrastructureRef.Name)) v1beta1conditions.MarkFalse(machine, clusterv1.MachineNodeHealthyV1Beta1Condition, clusterv1.WaitingForNodeRefV1Beta1Reason, clusterv1.ConditionSeverityInfo, "") return ctrl.Result{}, nil } @@ -83,12 +83,11 @@ func (r *Reconciler) reconcileNode(ctx context.Context, s *scope) (ctrl.Result, // Even if Status.NodeRef exists, continue to do the following checks to make sure Node is healthy node, err := r.getNode(ctx, remoteClient, machine.Spec.ProviderID) if err != nil { - if err == ErrNodeNotFound { + if errors.Is(err, ErrNodeNotFound) { if !s.machine.DeletionTimestamp.IsZero() { // Tolerate node not found when the machine is being deleted. return ctrl.Result{}, nil } - // While a NodeRef is set in the status, failing to get that node means the node is deleted. // If Status.NodeRef is not set before, node still can be in the provisioning state. if machine.Status.NodeRef.IsDefined() { @@ -96,7 +95,7 @@ func (r *Reconciler) reconcileNode(ctx context.Context, s *scope) (ctrl.Result, return ctrl.Result{}, errors.Wrapf(err, "no matching Node for Machine %q in namespace %q", machine.Name, machine.Namespace) } v1beta1conditions.MarkFalse(machine, clusterv1.MachineNodeHealthyV1Beta1Condition, clusterv1.NodeProvisioningV1Beta1Reason, clusterv1.ConditionSeverityWarning, "Waiting for a node with matching ProviderID to exist") - log.Info("Infrastructure provider reporting spec.providerID, matching Kubernetes node is not yet available", machine.Spec.InfrastructureRef.Kind, klog.KRef(machine.Namespace, machine.Spec.InfrastructureRef.Name), "providerID", machine.Spec.ProviderID) + log.Info("Infrastructure provider reporting spec.providerID, matching Kubernetes Node is not yet available", machine.Spec.InfrastructureRef.Kind, klog.KRef(machine.Namespace, machine.Spec.InfrastructureRef.Name), "providerID", machine.Spec.ProviderID) // No need to requeue here. Nodes emit an event that triggers reconciliation. return ctrl.Result{}, nil } @@ -112,7 +111,7 @@ func (r *Reconciler) reconcileNode(ctx context.Context, s *scope) (ctrl.Result, machine.Status.NodeRef = clusterv1.MachineNodeReference{ Name: s.node.Name, } - log.Info("Infrastructure provider reporting spec.providerID, Kubernetes node is now available", machine.Spec.InfrastructureRef.Kind, klog.KRef(machine.Namespace, machine.Spec.InfrastructureRef.Name), "providerID", machine.Spec.ProviderID, "Node", klog.KRef("", machine.Status.NodeRef.Name)) + log.Info("Infrastructure provider reporting spec.providerID, Kubernetes Node is now available", machine.Spec.InfrastructureRef.Kind, klog.KRef(machine.Namespace, machine.Spec.InfrastructureRef.Name), "providerID", machine.Spec.ProviderID, "Node", klog.KRef("", machine.Status.NodeRef.Name)) r.recorder.Event(machine, corev1.EventTypeNormal, "SuccessfulSetNodeRef", machine.Status.NodeRef.Name) } @@ -144,7 +143,7 @@ func (r *Reconciler) reconcileNode(ctx context.Context, s *scope) (ctrl.Result, _, nodeHadInterruptibleLabel := s.node.Labels[clusterv1.InterruptibleLabel] // Reconcile node taints - if err := r.patchNode(ctx, remoteClient, s.node, nodeLabels, nodeAnnotations, machine); err != nil { + if err := r.patchNode(ctx, remoteClient, s.node, nodeLabels, nodeAnnotations, machine, s.owningMachineSet, s.owningMachineDeployment); err != nil { return ctrl.Result{}, errors.Wrapf(err, "failed to reconcile Node %s", klog.KObj(s.node)) } if !nodeHadInterruptibleLabel && interruptible { @@ -247,7 +246,7 @@ func (r *Reconciler) getNode(ctx context.Context, c client.Reader, providerID st // PatchNode is required to workaround an issue on Node.Status.Address which is incorrectly annotated as patchStrategy=merge // and this causes SSA patch to fail in case there are two addresses with the same key https://github.com/kubernetes-sigs/cluster-api/issues/8417 -func (r *Reconciler) patchNode(ctx context.Context, remoteClient client.Client, node *corev1.Node, newLabels, newAnnotations map[string]string, m *clusterv1.Machine) error { +func (r *Reconciler) patchNode(ctx context.Context, remoteClient client.Client, node *corev1.Node, newLabels, newAnnotations map[string]string, m *clusterv1.Machine, ms *clusterv1.MachineSet, md *clusterv1.MachineDeployment) error { newNode := node.DeepCopy() // Adds the annotations from the Machine. @@ -336,74 +335,173 @@ func (r *Reconciler) patchNode(ctx context.Context, remoteClient client.Client, // Drop the NodeUninitializedTaint taint on the node given that we are reconciling labels. hasTaintChanges := taints.RemoveNodeTaint(newNode, clusterv1.NodeUninitializedTaint) + // Propagate taints set on the Machine to the Node. + var propagateTaintsChanges bool + if feature.Gates.Enabled(feature.MachineTaintPropagation) { + var err error + if propagateTaintsChanges, err = propagateMachineTaintsToNode(newNode, m.Spec.Taints); err != nil { + return errors.Wrapf(err, "failed to propagate Machine taints to Node %s", klog.KObj(node)) + } + } + // Set Taint to a node in an old MachineSet and unset Taint from a node in a new MachineSet - isOutdated, notFound, err := shouldNodeHaveOutdatedTaint(ctx, r.Client, m) + isOutdated, err := shouldNodeHaveOutdatedTaint(ms, md) if err != nil { return errors.Wrapf(err, "failed to check if Node %s is outdated", klog.KRef("", node.Name)) } - - // It is only possible to identify if we have to set or remove the NodeOutdatedRevisionTaint if shouldNodeHaveOutdatedTaint - // found all relevant objects. - // Example: when the MachineDeployment or Machineset can't be found due to a background deletion of objects. - if !notFound { - if isOutdated { - hasTaintChanges = taints.EnsureNodeTaint(newNode, clusterv1.NodeOutdatedRevisionTaint) || hasTaintChanges - } else { - hasTaintChanges = taints.RemoveNodeTaint(newNode, clusterv1.NodeOutdatedRevisionTaint) || hasTaintChanges - } + if isOutdated { + hasTaintChanges = taints.EnsureNodeTaint(newNode, clusterv1.NodeOutdatedRevisionTaint) || hasTaintChanges + } else { + hasTaintChanges = taints.RemoveNodeTaint(newNode, clusterv1.NodeOutdatedRevisionTaint) || hasTaintChanges } - if !hasAnnotationChanges && !hasLabelChanges && !hasTaintChanges { + if !hasAnnotationChanges && !hasLabelChanges && !hasTaintChanges && !propagateTaintsChanges { return nil } - return remoteClient.Patch(ctx, newNode, client.StrategicMergeFrom(node)) + // Use optimistic locking to avoid conflicts with other controllers. + mergeOptions := []client.MergeFromOption{client.MergeFromWithOptimisticLock{}} + + return remoteClient.Patch(ctx, newNode, client.StrategicMergeFrom(node, mergeOptions...)) } -// shouldNodeHaveOutdatedTaint tries to compare the revision of the owning MachineSet to the MachineDeployment. -// It returns notFound = true if the OwnerReference is not set or the APIServer returns NotFound for the MachineSet or MachineDeployment. -// Note: This three cases could happen during background deletion of objects. -func shouldNodeHaveOutdatedTaint(ctx context.Context, c client.Client, m *clusterv1.Machine) (outdated bool, notFound bool, err error) { - if _, hasLabel := m.Labels[clusterv1.MachineDeploymentNameLabel]; !hasLabel { - return false, false, nil - } +// propagateMachineTaintsToNode handles propagation of taints defined on a machine to a node. +// It makes use of the annotation clusterv1.TaintsFromMachineAnnotation to track which taints are owned by the controller. +// OnInitialization taints are only added to the node if the tracking annotation has not been set yet. +func propagateMachineTaintsToNode(node *corev1.Node, machineTaints []clusterv1.MachineTaint) (bool, error) { + changed := false + + // Get the value of the tracking annotation. If it is not set at all we also have to add the OnInitialization taints. + oldTaintsAnnotation, nodeTaintsInitialized := node.Annotations[clusterv1.TaintsFromMachineAnnotation] + + // ownedTaints contains all Always taints that the controller is owning. + ownedTaints := unmarshalMachineTaintsAnnotation(oldTaintsAnnotation) + + // newOwnedTaints will contain all Always taints from the current machine's spec. + newOwnedTaints := sets.New[string]() + onInitializationTaints := sets.New[string]() + + for _, taint := range machineTaints { + // Collect Always and OnInitialization taints to identify taints to delete. + // Separating Always taints so the tracking annotation can be updated accordingly. + switch taint.Propagation { + case clusterv1.MachineTaintPropagationAlways: + newOwnedTaints.Insert(fmt.Sprintf("%s:%s", taint.Key, taint.Effect)) + case clusterv1.MachineTaintPropagationOnInitialization: + onInitializationTaints.Insert(fmt.Sprintf("%s:%s", taint.Key, taint.Effect)) + } + + // Only add OnInitialization taints if the tracking annotation has not been set yet. + if taint.Propagation == clusterv1.MachineTaintPropagationOnInitialization && nodeTaintsInitialized { + continue + } - // Resolve the MachineSet name via owner references because the label value - // could also be a hash. - objKey, notFound, err := getOwnerMachineSetObjectKey(m.ObjectMeta) - if err != nil || notFound { - return false, notFound, err + // Ensure the taint is set on the node and has the correct value. + if changedTaints := ensureNodeTaintWithValue(node, convertMachineTaintToCoreV1Taint(taint)); changedTaints { + changed = true + } } - ms := &clusterv1.MachineSet{} - if err := c.Get(ctx, *objKey, ms); err != nil { - if apierrors.IsNotFound(err) { - return false, true, nil + + // Calculate ownedTaints - newOwnedTaints to identify old taints which need to be deleted from the node. + taintsToDelete := ownedTaints.Difference(newOwnedTaints).Difference(onInitializationTaints) + + // Remove all identified taints from the node. + for taintToDelete := range taintsToDelete { + if taintToDelete == "" { + continue + } + + splitted := strings.Split(taintToDelete, ":") + if len(splitted) != 2 { + return changed, fmt.Errorf("invalid taint format: %q", taintToDelete) + } + + if removedTaint := taints.RemoveNodeTaint(node, corev1.Taint{Key: splitted[0], Effect: corev1.TaintEffect(splitted[1])}); removedTaint { + changed = true } - return false, false, err } - md := &clusterv1.MachineDeployment{} - objKey = &client.ObjectKey{ - Namespace: m.Namespace, - Name: m.Labels[clusterv1.MachineDeploymentNameLabel], + + // Update the tracking annotation with newOwnedTaints.. + if newTaintsAnnotation := marshalMachineTaintsAnnotation(newOwnedTaints); !nodeTaintsInitialized || newTaintsAnnotation != oldTaintsAnnotation { + if node.Annotations == nil { + node.Annotations = map[string]string{} + } + node.Annotations[clusterv1.TaintsFromMachineAnnotation] = newTaintsAnnotation + changed = true } - if err := c.Get(ctx, *objKey, md); err != nil { - if apierrors.IsNotFound(err) { - return false, true, nil + + return changed, nil +} + +// ensureNodeTaintWithValue makes sure the node has the Taint with the expected value. +// It returns true if the taints are modified, false otherwise. +func ensureNodeTaintWithValue(node *corev1.Node, taint corev1.Taint) bool { + for i, currentTaint := range node.Spec.Taints { + if !taint.MatchTaint(¤tTaint) { + continue } - return false, false, err + + // Modify the taint if the value is different. + if currentTaint.Value != taint.Value { + node.Spec.Taints[i] = taint + return true + } + + // The taint is already set and has the correct value. + return false } + + // Add the taint if not present. + node.Spec.Taints = append(node.Spec.Taints, taint) + return true +} + +// marshalMachineTaintsAnnotation marshals the tracking annotation value. +func marshalMachineTaintsAnnotation(ownedTaints sets.Set[string]) string { + taints := ownedTaints.UnsortedList() + slices.Sort(taints) + + return strings.Join(taints, ",") +} + +// unmarshalMachineTaintsAnnotation unmarshals the tracking annotation value. +func unmarshalMachineTaintsAnnotation(annotationValue string) sets.Set[string] { + if annotationValue == "" { + return nil + } + + return sets.New(strings.Split(annotationValue, ",")...) +} + +// convertMachineTaintToCoreV1Taint converts a MachineTaint to a corev1.Taint. +func convertMachineTaintToCoreV1Taint(machineTaint clusterv1.MachineTaint) corev1.Taint { + return corev1.Taint{ + Key: machineTaint.Key, + Value: machineTaint.Value, + Effect: machineTaint.Effect, + } +} + +// shouldNodeHaveOutdatedTaint tries to compare the revision of the owning MachineSet to the MachineDeployment. +// It returns notFound = true if the OwnerReference is not set or the APIServer returns NotFound for the MachineSet or MachineDeployment. +// Note: This three cases could happen during background deletion of objects. +func shouldNodeHaveOutdatedTaint(ms *clusterv1.MachineSet, md *clusterv1.MachineDeployment) (outdated bool, err error) { + if ms == nil || md == nil { + return false, nil + } + msRev, err := mdutil.Revision(ms) if err != nil { - return false, false, err + return false, err } mdRev, err := mdutil.Revision(md) if err != nil { - return false, false, err + return false, err } if msRev < mdRev { - return true, false, nil + return true, nil } - return false, false, nil + return false, nil } func getOwnerMachineSetObjectKey(obj metav1.ObjectMeta) (*client.ObjectKey, bool, error) { diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_phases.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_phases.go index 27380dce14..6cedb6d2a8 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_phases.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_phases.go @@ -37,6 +37,7 @@ import ( capierrors "sigs.k8s.io/cluster-api/errors" "sigs.k8s.io/cluster-api/internal/contract" "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/conditions" v1beta1conditions "sigs.k8s.io/cluster-api/util/conditions/deprecated/v1beta1" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/cluster-api/util/predicates" @@ -212,9 +213,12 @@ func (r *Reconciler) reconcileBootstrap(ctx context.Context, s *scope) (ctrl.Res // If the data secret was not created yet, return. if !dataSecretCreated { - log.Info(fmt.Sprintf("Waiting for bootstrap provider to generate data secret and set %s", - contract.Bootstrap().DataSecretCreated(contractVersion).Path().String()), - s.bootstrapConfig.GetKind(), klog.KObj(s.bootstrapConfig)) + // Only log if the Machine is a control plane Machine or the Cluster is already initialized to reduce noise. + if util.IsControlPlaneMachine(m) || conditions.IsTrue(s.cluster, clusterv1.ClusterControlPlaneInitializedCondition) { + log.Info(fmt.Sprintf("Waiting for bootstrap provider to generate data secret and set %s", + contract.Bootstrap().DataSecretCreated(contractVersion).Path().String()), + s.bootstrapConfig.GetKind(), klog.KObj(s.bootstrapConfig)) + } return ctrl.Result{}, nil } @@ -309,21 +313,29 @@ func (r *Reconciler) reconcileInfrastructure(ctx context.Context, s *scope) (ctr // If the InfrastructureMachine is not provisioned (and it wasn't already provisioned before), return. if !provisioned && !ptr.Deref(m.Status.Initialization.InfrastructureProvisioned, false) { - log.Info(fmt.Sprintf("Waiting for infrastructure provider to create machine infrastructure and set %s", - contract.InfrastructureMachine().Provisioned(contractVersion).Path().String()), - s.infraMachine.GetKind(), klog.KObj(s.infraMachine)) + // Only log if the Machine is a control plane Machine or the Cluster is already initialized to reduce noise. + if util.IsControlPlaneMachine(m) || conditions.IsTrue(s.cluster, clusterv1.ClusterControlPlaneInitializedCondition) { + log.Info(fmt.Sprintf("Waiting for infrastructure provider to set %s on %s", + contract.InfrastructureMachine().Provisioned(contractVersion).Path().String(), s.infraMachine.GetKind()), + s.infraMachine.GetKind(), klog.KObj(s.infraMachine)) + } return ctrl.Result{}, nil } // Get providerID from the InfrastructureMachine (intentionally not setting it on the Machine yet). - var providerID *string - if providerID, err = contract.InfrastructureMachine().ProviderID().Get(s.infraMachine); err != nil { - return ctrl.Result{}, errors.Wrapf(err, "failed to read providerID from %s %s", - s.infraMachine.GetKind(), klog.KObj(s.infraMachine)) - } else if *providerID == "" { - return ctrl.Result{}, errors.Errorf("got empty %s field from %s %s", + providerID, err := contract.InfrastructureMachine().ProviderID().Get(s.infraMachine) + switch { + case err != nil && !errors.Is(err, contract.ErrFieldNotFound): + return ctrl.Result{}, errors.Wrapf(err, "failed to read %s from %s %s", contract.InfrastructureMachine().ProviderID().Path().String(), s.infraMachine.GetKind(), klog.KObj(s.infraMachine)) + case ptr.Deref(providerID, "") == "": + log.Info(fmt.Sprintf("Waiting for infrastructure provider to set %s on %s", + contract.InfrastructureMachine().ProviderID().Path().String(), s.infraMachine.GetKind()), + s.infraMachine.GetKind(), klog.KObj(s.infraMachine)) + // Slow down reconcile frequency, provisioning infrastructure takes some time. + r.controller.DeferNextReconcileForObject(s.machine, time.Now().Add(5*time.Second)) + return ctrl.Result{}, nil // Note: Requeue is not needed, changes to InfraMachine trigger another reconcile. } // Get and set addresses from the InfrastructureMachine. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_status.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_status.go index 59c70cac01..8f2ef002a7 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_status.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machine/machine_controller_status.go @@ -34,6 +34,8 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/controllers/clustercache" "sigs.k8s.io/cluster-api/internal/contract" + "sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil" + "sigs.k8s.io/cluster-api/internal/util/inplace" "sigs.k8s.io/cluster-api/util/conditions" ) @@ -42,7 +44,7 @@ import ( // machine being partially deleted but also for running machines being disrupted e.g. by deleting the node. // Additionally, this func should ensure that the conditions managed by this controller are always set in order to // comply with the recommendation in the Kubernetes API guidelines. -func (r *Reconciler) updateStatus(ctx context.Context, s *scope) { +func (r *Reconciler) updateStatus(ctx context.Context, s *scope) ctrl.Result { // Update status from the Bootstrap Config external resource. // Note: some of the status fields derived from the Bootstrap Config are managed in reconcileBootstrap, e.g. status.BootstrapReady, etc. // here we are taking care only of the delta (condition). @@ -64,11 +66,13 @@ func (r *Reconciler) updateStatus(ctx context.Context, s *scope) { // in reconcileDelete (e.g. status.Deletion nested fields), and also in the defer patch at the end of the main reconcile loop (status.ObservedGeneration) etc. // Note: also other controllers adds conditions to the machine object (machine's owner controller sets the UpToDate condition, // MHC controller sets HealthCheckSucceeded and OwnerRemediated conditions, KCP sets conditions about etcd and control plane pods). - setDeletingCondition(ctx, s.machine, s.reconcileDeleteExecuted, s.deletingReason, s.deletingMessage) + setDeletingCondition(ctx, s.machine, s.deletingReason, s.deletingMessage) + setUpdatingCondition(ctx, s.machine, s.updatingReason, s.updatingMessage) + setUpToDateCondition(ctx, s.machine, s.owningMachineSet, s.owningMachineDeployment) setReadyCondition(ctx, s.machine) - setAvailableCondition(ctx, s.machine) - setMachinePhaseAndLastUpdated(ctx, s.machine) + + return setAvailableCondition(ctx, s.machine) } func setBootstrapReadyCondition(_ context.Context, machine *clusterv1.Machine, bootstrapConfig *unstructured.Unstructured, bootstrapConfigIsNotFound bool) { @@ -609,7 +613,7 @@ func transformControlPlaneAndEtcdConditions(messages []string) []string { return out } -func setDeletingCondition(_ context.Context, machine *clusterv1.Machine, reconcileDeleteExecuted bool, deletingReason, deletingMessage string) { +func setDeletingCondition(_ context.Context, machine *clusterv1.Machine, deletingReason, deletingMessage string) { if machine.DeletionTimestamp.IsZero() { conditions.Set(machine, metav1.Condition{ Type: clusterv1.MachineDeletingCondition, @@ -619,12 +623,6 @@ func setDeletingCondition(_ context.Context, machine *clusterv1.Machine, reconci return } - if !reconcileDeleteExecuted { - // Don't update the Deleting condition if reconcileDelete was not executed (e.g. - // because of rate-limiting). - return - } - conditions.Set(machine, metav1.Condition{ Type: clusterv1.MachineDeletingCondition, Status: metav1.ConditionTrue, @@ -633,17 +631,117 @@ func setDeletingCondition(_ context.Context, machine *clusterv1.Machine, reconci }) } +func setUpdatingCondition(_ context.Context, machine *clusterv1.Machine, updatingReason, updatingMessage string) { + if inplace.IsUpdateInProgress(machine) { + if updatingReason == "" { + updatingReason = clusterv1.MachineInPlaceUpdatingReason + } + if updatingMessage == "" { + updatingMessage = "In-place update in progress" + } + conditions.Set(machine, metav1.Condition{ + Type: clusterv1.MachineUpdatingCondition, + Status: metav1.ConditionTrue, + Reason: updatingReason, + Message: updatingMessage, + }) + return + } + + conditions.Set(machine, metav1.Condition{ + Type: clusterv1.MachineUpdatingCondition, + Status: metav1.ConditionFalse, + Reason: clusterv1.MachineNotUpdatingReason, + }) +} + +func setUpToDateCondition(_ context.Context, m *clusterv1.Machine, ms *clusterv1.MachineSet, md *clusterv1.MachineDeployment) { + // If the current Machine is a stand-alone machine or a machine controlled by a stand-alone MachineSet, + // do not set an up-to-date condition on Machines, allowing tools managing higher level abstractions to set this condition. + // Note: This is also consistent with the fact that the MachineSet primarily takes care of the number of Machine + // replicas, it doesn't reconcile them (even if we have a few exceptions like in-place propagation of a few selected + // fields and remediation). + // Note: The Machine controller is computing upToDate condition only for worker machines. + // This difference exists for two reasons: + // - We would like to avoid race conditions that might happen by computing machine's conditions on different controllers, + // and most specifically for those conditions that are considered in the MachineSet controller and in the MachineDeployment + // controller e.g. to orchestrate rollouts with in-place updates. + // - Core CAPI is not aware of specific details of every control plane implementation, so it is not possible to + // compute the UpToDateCondition for control plane machines. + if ms == nil || md == nil { + return + } + + // Determine current and desired state. + // If the current MachineSet is owned by a MachineDeployment, we mirror what is implemented in the MachineDeployment controller + // to trigger rollouts (by creating new MachineSets). + // More specifically: + // - desired state for the Machine is the spec.Template of the MachineDeployment + // - current state for the Machine is the spec.Template of the MachineSet who owns the Machine + // Note: We are intentionally considering current spec from the MachineSet instead of spec from the Machine itself in + // order to surface info consistent with what the MachineDeployment controller uses to take decisions about rollouts. + // The downside is that the system will ignore out of band changes applied to controlled Machines, which is + // considered an acceptable trade-off given that out of band changes are the exception (users should not change + // objects owned by the system). + // However, if out of band changes happen, at least the system will ignore out of band changes consistently, both in the + // MachineDeployment controller and in the condition computed here. + current := &ms.Spec.Template + desired := &md.Spec.Template + + upToDate, notUpToDateResult := mdutil.MachineTemplateUpToDate(current, desired) + + if !md.Spec.Rollout.After.IsZero() && md.Spec.Rollout.After.Before(ptr.To(metav1.Now())) && ms.CreationTimestamp.Before(ptr.To(md.Spec.Rollout.After)) { + upToDate = false + notUpToDateResult.ConditionMessages = append(notUpToDateResult.ConditionMessages, "MachineDeployment spec.rolloutAfter expired") + } + + if !upToDate { + for i := range notUpToDateResult.ConditionMessages { + notUpToDateResult.ConditionMessages[i] = fmt.Sprintf("* %s", notUpToDateResult.ConditionMessages[i]) + } + conditions.Set(m, metav1.Condition{ + Type: clusterv1.MachineUpToDateCondition, + Status: metav1.ConditionFalse, + Reason: clusterv1.MachineNotUpToDateReason, + // Note: the code computing the message for MachineDeployment's RolloutOut condition is making assumptions on the format/content of this message. + Message: strings.Join(notUpToDateResult.ConditionMessages, "\n"), + }) + return + } + + if c := conditions.Get(m, clusterv1.MachineUpdatingCondition); c != nil && c.Status == metav1.ConditionTrue { + msg := "* In-place update in progress" + if c.Message != "" { + msg = fmt.Sprintf("* %s", c.Message) + } + conditions.Set(m, metav1.Condition{ + Type: clusterv1.MachineUpToDateCondition, + Status: metav1.ConditionFalse, + Reason: clusterv1.MachineUpToDateUpdatingReason, + Message: msg, + }) + return + } + + conditions.Set(m, metav1.Condition{ + Type: clusterv1.MachineUpToDateCondition, + Status: metav1.ConditionTrue, + Reason: clusterv1.MachineUpToDateReason, + }) +} + func setReadyCondition(ctx context.Context, machine *clusterv1.Machine) { log := ctrl.LoggerFrom(ctx) forConditionTypes := conditions.ForConditionTypes{ clusterv1.MachineDeletingCondition, + clusterv1.MachineUpdatingCondition, clusterv1.MachineBootstrapConfigReadyCondition, clusterv1.MachineInfrastructureReadyCondition, clusterv1.MachineNodeHealthyCondition, clusterv1.MachineHealthCheckSucceededCondition, } - negativePolarityConditionTypes := []string{clusterv1.MachineDeletingCondition} + negativePolarityConditionTypes := []string{clusterv1.MachineDeletingCondition, clusterv1.MachineUpdatingCondition} for _, g := range machine.Spec.ReadinessGates { forConditionTypes = append(forConditionTypes, g.ConditionType) if g.Polarity == clusterv1.NegativePolarityCondition { @@ -666,9 +764,10 @@ func setReadyCondition(ctx context.Context, machine *clusterv1.Machine) { negativePolarityConditionTypes: negativePolarityConditionTypes, }, }, - // Instruct summary to consider Deleting condition with negative polarity. + // Instruct summary to consider Deleting and updating condition with negative polarity. conditions.NegativePolarityConditionTypes{ clusterv1.MachineDeletingCondition, + clusterv1.MachineUpdatingCondition, }, } @@ -753,7 +852,7 @@ func calculateDeletingConditionForSummary(machine *clusterv1.Machine) conditions } } -func setAvailableCondition(ctx context.Context, machine *clusterv1.Machine) { +func setAvailableCondition(ctx context.Context, machine *clusterv1.Machine) ctrl.Result { log := ctrl.LoggerFrom(ctx) readyCondition := conditions.Get(machine, clusterv1.MachineReadyCondition) @@ -767,7 +866,7 @@ func setAvailableCondition(ctx context.Context, machine *clusterv1.Machine) { Reason: clusterv1.MachineAvailableInternalErrorReason, Message: "Please check controller logs for errors", }) - return + return ctrl.Result{} } if readyCondition.Status != metav1.ConditionTrue { @@ -776,16 +875,17 @@ func setAvailableCondition(ctx context.Context, machine *clusterv1.Machine) { Status: metav1.ConditionFalse, Reason: clusterv1.MachineNotReadyReason, }) - return + return ctrl.Result{} } - if time.Since(readyCondition.LastTransitionTime.Time) >= time.Duration(ptr.Deref(machine.Spec.MinReadySeconds, 0))*time.Second { + t := time.Since(readyCondition.LastTransitionTime.Time) - time.Duration(ptr.Deref(machine.Spec.MinReadySeconds, 0))*time.Second + if t >= 0 { conditions.Set(machine, metav1.Condition{ Type: clusterv1.MachineAvailableCondition, Status: metav1.ConditionTrue, Reason: clusterv1.MachineAvailableReason, }) - return + return ctrl.Result{} } conditions.Set(machine, metav1.Condition{ @@ -793,6 +893,7 @@ func setAvailableCondition(ctx context.Context, machine *clusterv1.Machine) { Status: metav1.ConditionFalse, Reason: clusterv1.MachineWaitingForMinReadySecondsReason, }) + return ctrl.Result{RequeueAfter: -t} } func setMachinePhaseAndLastUpdated(_ context.Context, m *clusterv1.Machine) { @@ -818,6 +919,10 @@ func setMachinePhaseAndLastUpdated(_ context.Context, m *clusterv1.Machine) { m.Status.SetTypedPhase(clusterv1.MachinePhaseRunning) } + if conditions.IsTrue(m, clusterv1.MachineUpdatingCondition) { + m.Status.SetTypedPhase(clusterv1.MachinePhaseUpdating) + } + // Set the phase to "deleting" if the deletion timestamp is set. if !m.DeletionTimestamp.IsZero() { m.Status.SetTypedPhase(clusterv1.MachinePhaseDeleting) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_canupdatemachineset.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_canupdatemachineset.go new file mode 100644 index 0000000000..f25a13ef83 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_canupdatemachineset.go @@ -0,0 +1,383 @@ +/* +Copyright 2025 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 machinedeployment + +import ( + "cmp" + "context" + "fmt" + "strings" + + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" + "sigs.k8s.io/cluster-api/controllers/external" + "sigs.k8s.io/cluster-api/internal/util/compare" + "sigs.k8s.io/cluster-api/internal/util/inplace" + "sigs.k8s.io/cluster-api/internal/util/patch" +) + +// CanUpdateMachineSetCacheEntry is an entry for the CanUpdateMachineSet hook cache. +type CanUpdateMachineSetCacheEntry struct { + OldMS client.ObjectKey + NewMS client.ObjectKey + CanUpdateMachineSet bool +} + +// Key returns the cache key of a CanUpdateMachineSetCacheEntry. +func (r CanUpdateMachineSetCacheEntry) Key() string { + return fmt.Sprintf("%s => %s", r.OldMS, r.NewMS) +} + +func (p *rolloutPlanner) canUpdateMachineSetInPlace(ctx context.Context, oldMS, newMS *clusterv1.MachineSet) (bool, error) { + if p.overrideCanUpdateMachineSetInPlace != nil { + return p.overrideCanUpdateMachineSetInPlace(ctx, oldMS, newMS) + } + + log := ctrl.LoggerFrom(ctx).WithValues("MachineSet", klog.KObj(oldMS)) + + templateObjects, err := p.getTemplateObjects(ctx, oldMS, newMS) + if err != nil { + return false, err + } + + // MachineSet cannot be updated in-place if the getTemplateObjects func was not able to get all InfraMachineTemplates. + if templateObjects.CurrentInfraMachineTemplate == nil || + templateObjects.DesiredInfraMachineTemplate == nil { + return false, nil + } + // MachineSet cannot be updated in-place if the BootstrapConfigTemplate is set on the oldMS but not on the newMS or vice versa. + if (templateObjects.CurrentBootstrapConfigTemplate == nil && templateObjects.DesiredBootstrapConfigTemplate != nil) || + (templateObjects.CurrentBootstrapConfigTemplate != nil && templateObjects.DesiredBootstrapConfigTemplate == nil) { + return false, nil + } + + extensionHandlers, err := p.RuntimeClient.GetAllExtensions(ctx, runtimehooksv1.CanUpdateMachineSet, oldMS) + if err != nil { + return false, err + } + // MachineSet cannot be updated in-place if no CanUpdateMachineSet extensions are registered. + if len(extensionHandlers) == 0 { + return false, nil + } + if len(extensionHandlers) > 1 { + return false, errors.Errorf("found multiple CanUpdateMachineSet hooks (%s): only one hook is supported", strings.Join(extensionHandlers, ",")) + } + + entry := CanUpdateMachineSetCacheEntry{ + OldMS: client.ObjectKeyFromObject(oldMS), + NewMS: client.ObjectKeyFromObject(newMS), + } + + if cacheEntry, ok := p.canUpdateMachineSetCache.Has(entry.Key()); ok { + if cacheEntry.CanUpdateMachineSet { + log.V(5).Info(fmt.Sprintf("MachineSet %s can be updated in-place by extensions (cached)", oldMS.Name)) + } else { + log.V(5).Info(fmt.Sprintf("MachineSet %s cannot be updated in-place by extensions (cached)", oldMS.Name)) + } + return cacheEntry.CanUpdateMachineSet, nil + } + + canUpdateMachineSet, reasons, err := p.canExtensionsUpdateMachineSet(ctx, oldMS, newMS, templateObjects, extensionHandlers) + if err != nil { + return false, err + } + entry.CanUpdateMachineSet = canUpdateMachineSet + p.canUpdateMachineSetCache.Add(entry) + + if !canUpdateMachineSet { + log.Info(fmt.Sprintf("MachineSet %s cannot be updated in-place by extensions", oldMS.Name), "reason", strings.Join(reasons, ",")) + return false, nil + } + log.Info(fmt.Sprintf("MachineSet %s can be updated in-place by extensions", oldMS.Name)) + return true, nil +} + +// canExtensionsUpdateMachineSet calls CanUpdateMachineSet extensions to decide if a MachineSet can be updated in-place. +// Note: This is following the same general structure that is used in the Apply func in +// internal/controllers/topology/cluster/patches/engine.go. +func (p *rolloutPlanner) canExtensionsUpdateMachineSet(ctx context.Context, oldMS, newMS *clusterv1.MachineSet, templateObjects *templateObjects, extensionHandlers []string) (bool, []string, error) { + if p.overrideCanExtensionsUpdateMachineSet != nil { + return p.overrideCanExtensionsUpdateMachineSet(ctx, oldMS, newMS, templateObjects, extensionHandlers) + } + + log := ctrl.LoggerFrom(ctx) + + // Create the CanUpdateMachineSet request. + req, err := createRequest(oldMS, newMS, templateObjects) + if err != nil { + return false, nil, errors.Wrapf(err, "failed to generate CanUpdateMachineSet request") + } + + var reasons []string + for _, extensionHandler := range extensionHandlers { + // Call CanUpdateMachineSet extension. + resp := &runtimehooksv1.CanUpdateMachineSetResponse{} + if err := p.RuntimeClient.CallExtension(ctx, runtimehooksv1.CanUpdateMachineSet, oldMS, extensionHandler, req, resp); err != nil { + return false, nil, err + } + + // Apply patches from the CanUpdateMachineSet response to the request. + if err := applyPatchesToRequest(ctx, req, resp); err != nil { + return false, nil, errors.Wrapf(err, "failed to apply patches from extension %s to the CanUpdateMachineSet request", extensionHandler) + } + + // Check if current and desired objects are now matching. + var matches bool + matches, reasons, err = matchesMachineSet(req) + if err != nil { + return false, nil, errors.Wrapf(err, "failed to compare current and desired objects after calling extension %s", extensionHandler) + } + if matches { + return true, nil, nil + } + log.V(5).Info(fmt.Sprintf("MachineSet cannot be updated in-place yet after calling extension %s: %s", extensionHandler, strings.Join(reasons, ",")), "MachineSet", klog.KObj(oldMS)) + } + + return false, reasons, nil +} + +func createRequest(oldMS, newMS *clusterv1.MachineSet, templateObjects *templateObjects) (*runtimehooksv1.CanUpdateMachineSetRequest, error) { + // DeepCopy MachineSets to avoid mutations. + currentMachineSetForDiff := oldMS.DeepCopy() + currentBootstrapConfigTemplateForDiff := templateObjects.CurrentBootstrapConfigTemplate + currentInfraMachineTemplateForDiff := templateObjects.CurrentInfraMachineTemplate + + desiredMachineSetForDiff := newMS.DeepCopy() + desiredBootstrapConfigTemplateForDiff := templateObjects.DesiredBootstrapConfigTemplate + desiredInfraMachineTemplateForDiff := templateObjects.DesiredInfraMachineTemplate + + // Cleanup objects and create request. + req := &runtimehooksv1.CanUpdateMachineSetRequest{ + Current: runtimehooksv1.CanUpdateMachineSetRequestObjects{ + MachineSet: *cleanupMachineSet(currentMachineSetForDiff), + }, + Desired: runtimehooksv1.CanUpdateMachineSetRequestObjects{ + MachineSet: *cleanupMachineSet(desiredMachineSetForDiff), + }, + } + var err error + if currentBootstrapConfigTemplateForDiff != nil { + req.Current.BootstrapConfigTemplate, err = patch.ConvertToRawExtension(cleanupUnstructured(currentBootstrapConfigTemplateForDiff)) + if err != nil { + return nil, err + } + } + if desiredBootstrapConfigTemplateForDiff != nil { + req.Desired.BootstrapConfigTemplate, err = patch.ConvertToRawExtension(cleanupUnstructured(desiredBootstrapConfigTemplateForDiff)) + if err != nil { + return nil, err + } + } + req.Current.InfrastructureMachineTemplate, err = patch.ConvertToRawExtension(cleanupUnstructured(currentInfraMachineTemplateForDiff)) + if err != nil { + return nil, err + } + req.Desired.InfrastructureMachineTemplate, err = patch.ConvertToRawExtension(cleanupUnstructured(desiredInfraMachineTemplateForDiff)) + if err != nil { + return nil, err + } + + return req, nil +} + +func cleanupMachineSet(machine *clusterv1.MachineSet) *clusterv1.MachineSet { + return &clusterv1.MachineSet{ + // Set GVK because object is later marshalled with json.Marshal. + TypeMeta: metav1.TypeMeta{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "MachineSet", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: machine.Name, + Namespace: machine.Namespace, + Labels: machine.Labels, + Annotations: machine.Annotations, + }, + Spec: *machine.Spec.DeepCopy(), + } +} + +func cleanupUnstructured(u *unstructured.Unstructured) *unstructured.Unstructured { + cleanedUpU := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": u.GetAPIVersion(), + "kind": u.GetKind(), + "spec": u.Object["spec"], + }, + } + cleanedUpU.SetName(u.GetName()) + cleanedUpU.SetNamespace(u.GetNamespace()) + cleanedUpU.SetLabels(u.GetLabels()) + cleanedUpU.SetAnnotations(u.GetAnnotations()) + return cleanedUpU +} + +func applyPatchesToRequest(ctx context.Context, req *runtimehooksv1.CanUpdateMachineSetRequest, resp *runtimehooksv1.CanUpdateMachineSetResponse) error { + if resp.MachineSetPatch.IsDefined() { + if err := patch.ApplyPatchToTypedObject(ctx, &req.Current.MachineSet, resp.MachineSetPatch, "spec.template.spec"); err != nil { + return err + } + } + + if resp.BootstrapConfigTemplatePatch.IsDefined() && req.Current.BootstrapConfigTemplate.Object != nil { + if _, err := patch.ApplyPatchToObject(ctx, &req.Current.BootstrapConfigTemplate, resp.BootstrapConfigTemplatePatch, "spec.template.spec"); err != nil { + return err + } + } + + if resp.InfrastructureMachineTemplatePatch.IsDefined() { + if _, err := patch.ApplyPatchToObject(ctx, &req.Current.InfrastructureMachineTemplate, resp.InfrastructureMachineTemplatePatch, "spec.template.spec"); err != nil { + return err + } + } + + return nil +} + +func matchesMachineSet(req *runtimehooksv1.CanUpdateMachineSetRequest) (bool, []string, error) { + var reasons []string + match, diff, err := matchesMachineSetSpec(&req.Current.MachineSet, &req.Desired.MachineSet) + if err != nil { + return false, nil, errors.Wrapf(err, "failed to match MachineSet") + } + if !match { + reasons = append(reasons, fmt.Sprintf("MachineSet cannot be updated in-place: %s", diff)) + } + if req.Current.BootstrapConfigTemplate.Object != nil && req.Desired.BootstrapConfigTemplate.Object != nil { + match, diff, err = matchesUnstructuredSpec(req.Current.BootstrapConfigTemplate, req.Desired.BootstrapConfigTemplate) + if err != nil { + return false, nil, errors.Wrapf(err, "failed to match %s", req.Current.BootstrapConfigTemplate.Object.GetObjectKind().GroupVersionKind().Kind) + } + if !match { + reasons = append(reasons, fmt.Sprintf("%s cannot be updated in-place: %s", req.Current.BootstrapConfigTemplate.Object.GetObjectKind().GroupVersionKind().Kind, diff)) + } + } + match, diff, err = matchesUnstructuredSpec(req.Current.InfrastructureMachineTemplate, req.Desired.InfrastructureMachineTemplate) + if err != nil { + return false, nil, errors.Wrapf(err, "failed to match %s", req.Current.InfrastructureMachineTemplate.Object.GetObjectKind().GroupVersionKind().Kind) + } + if !match { + reasons = append(reasons, fmt.Sprintf("%s cannot be updated in-place: %s", req.Current.InfrastructureMachineTemplate.Object.GetObjectKind().GroupVersionKind().Kind, diff)) + } + + if len(reasons) > 0 { + return false, reasons, nil + } + + return true, nil, nil +} + +func matchesMachineSetSpec(patched, desired *clusterv1.MachineSet) (equal bool, diff string, matchErr error) { + // Note: Wrapping MachineSet specs in a MachineSet for proper formatting of the diff. + return compare.Diff( + &clusterv1.MachineSet{ + Spec: clusterv1.MachineSetSpec{ + Template: clusterv1.MachineTemplateSpec{ + Spec: *inplace.CleanupMachineSpecForDiff(&patched.Spec.Template.Spec), + }, + }, + }, &clusterv1.MachineSet{ + Spec: clusterv1.MachineSetSpec{ + Template: clusterv1.MachineTemplateSpec{ + Spec: *inplace.CleanupMachineSpecForDiff(&desired.Spec.Template.Spec), + }, + }, + }, + ) +} + +func matchesUnstructuredSpec(patched, desired runtime.RawExtension) (equal bool, diff string, matchErr error) { + // Note: Both patched and desired objects are always Unstructured as createRequest and + // applyPatchToObject are always setting objects as Unstructured. + patchedUnstructured, ok := patched.Object.(*unstructured.Unstructured) + if !ok { + return false, "", errors.Errorf("patched object is not an Unstructured") + } + desiredUnstructured, ok := desired.Object.(*unstructured.Unstructured) + if !ok { + return false, "", errors.Errorf("desired object is not an Unstructured") + } + + // Note: Wrapping Unstructured spec.template.specs in an Unstructured for proper formatting of the diff. + patchedSpecTemplateSpec, foundPatched, err := unstructured.NestedFieldNoCopy(patchedUnstructured.Object, "spec", "template", "spec") + if err != nil { + return false, "", errors.Errorf("could not read spec.template.spec from patched object") + } + desiredSpecTemplateSpec, foundDesired, err := unstructured.NestedFieldNoCopy(desiredUnstructured.Object, "spec", "template", "spec") + if err != nil { + return false, "", errors.Errorf("could not read spec.template.spec from desired object") + } + + cleanedUpPatchedUnstructured := &unstructured.Unstructured{Object: map[string]interface{}{}} + if foundPatched { + if err := unstructured.SetNestedField(cleanedUpPatchedUnstructured.Object, patchedSpecTemplateSpec, "spec", "template", "spec"); err != nil { + return false, "", errors.Errorf("could not write spec.template.spec to patched object for comparison") + } + } + cleanedUpDesiredUnstructured := &unstructured.Unstructured{Object: map[string]interface{}{}} + if foundDesired { + if err := unstructured.SetNestedField(cleanedUpDesiredUnstructured.Object, desiredSpecTemplateSpec, "spec", "template", "spec"); err != nil { + return false, "", errors.Errorf("could not write spec.template.spec to desired object for comparison") + } + } + + return compare.Diff(cleanedUpPatchedUnstructured, cleanedUpDesiredUnstructured) +} + +type templateObjects struct { + CurrentInfraMachineTemplate *unstructured.Unstructured + DesiredInfraMachineTemplate *unstructured.Unstructured + CurrentBootstrapConfigTemplate *unstructured.Unstructured + DesiredBootstrapConfigTemplate *unstructured.Unstructured +} + +func (p *rolloutPlanner) getTemplateObjects(ctx context.Context, oldMS, newMS *clusterv1.MachineSet) (*templateObjects, error) { + templateObjects := &templateObjects{} + var err error + + templateObjects.CurrentInfraMachineTemplate, err = external.GetObjectFromContractVersionedRef(ctx, p.Client, oldMS.Spec.Template.Spec.InfrastructureRef, oldMS.Namespace) + if err != nil { + return nil, errors.Wrapf(err, "failed to get %s from MachineSet %s", cmp.Or(oldMS.Spec.Template.Spec.InfrastructureRef.Kind, "InfrastructureMachineTemplate"), oldMS.Name) + } + templateObjects.DesiredInfraMachineTemplate, err = external.GetObjectFromContractVersionedRef(ctx, p.Client, newMS.Spec.Template.Spec.InfrastructureRef, newMS.Namespace) + if err != nil { + return nil, errors.Wrapf(err, "failed to get %s from MachineSet %s", cmp.Or(newMS.Spec.Template.Spec.InfrastructureRef.Kind, "InfrastructureMachineTemplate"), newMS.Name) + } + + if oldMS.Spec.Template.Spec.Bootstrap.ConfigRef.IsDefined() { + templateObjects.CurrentBootstrapConfigTemplate, err = external.GetObjectFromContractVersionedRef(ctx, p.Client, oldMS.Spec.Template.Spec.Bootstrap.ConfigRef, oldMS.Namespace) + if err != nil { + return nil, errors.Wrapf(err, "failed to get %s from MachineSet %s", cmp.Or(oldMS.Spec.Template.Spec.Bootstrap.ConfigRef.Kind, "BootstrapConfigTemplate"), oldMS.Name) + } + } + if newMS.Spec.Template.Spec.Bootstrap.ConfigRef.IsDefined() { + templateObjects.DesiredBootstrapConfigTemplate, err = external.GetObjectFromContractVersionedRef(ctx, p.Client, newMS.Spec.Template.Spec.Bootstrap.ConfigRef, newMS.Namespace) + if err != nil { + return nil, errors.Wrapf(err, "failed to get %s from MachineSet %s", cmp.Or(newMS.Spec.Template.Spec.Bootstrap.ConfigRef.Kind, "BootstrapConfigTemplate"), newMS.Name) + } + } + + return templateObjects, nil +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_controller.go index 9e12bcd7ac..dd99b1f37f 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_controller.go @@ -19,6 +19,7 @@ package machinedeployment import ( "context" "fmt" + "sort" "strings" "github.com/pkg/errors" @@ -31,7 +32,6 @@ import ( "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -39,8 +39,14 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/controllers/external" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" + "sigs.k8s.io/cluster-api/feature" + clientutil "sigs.k8s.io/cluster-api/internal/util/client" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/internal/util/ssa" "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/cache" + "sigs.k8s.io/cluster-api/util/collections" v1beta1conditions "sigs.k8s.io/cluster-api/util/conditions/deprecated/v1beta1" "sigs.k8s.io/cluster-api/util/finalizers" clog "sigs.k8s.io/cluster-api/util/log" @@ -68,20 +74,26 @@ const machineDeploymentManagerName = "capi-machinedeployment" // Reconciler reconciles a MachineDeployment object. type Reconciler struct { - Client client.Client - APIReader client.Reader + Client client.Client + APIReader client.Reader + RuntimeClient runtimeclient.Client // WatchFilterValue is the label value used to filter events prior to reconciliation. WatchFilterValue string recorder record.EventRecorder ssaCache ssa.Cache + + canUpdateMachineSetCache cache.Cache[CanUpdateMachineSetCacheEntry] } func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { if r.Client == nil || r.APIReader == nil { return errors.New("Client and APIReader must not be nil") } + if feature.Gates.Enabled(feature.InPlaceUpdates) && r.RuntimeClient == nil { + return errors.New("RuntimeClient must not be nil when InPlaceUpdates feature gate is enabled") + } predicateLog := ctrl.LoggerFrom(ctx).WithValues("controller", "machinedeployment") clusterToMachineDeployments, err := util.ClusterToTypedObjectsMapper(mgr.GetClient(), &clusterv1.MachineDeploymentList{}, mgr.GetScheme()) @@ -89,30 +101,27 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt return err } - err = ctrl.NewControllerManagedBy(mgr). + err = capicontrollerutil.NewControllerManagedBy(mgr, predicateLog). For(&clusterv1.MachineDeployment{}). - Owns(&clusterv1.MachineSet{}, builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog))). + Owns(&clusterv1.MachineSet{}). // Watches enqueues MachineDeployment for corresponding MachineSet resources, if no managed controller reference (owner) exists. Watches( &clusterv1.MachineSet{}, handler.EnqueueRequestsFromMapFunc(r.MachineSetToDeployments), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog)), ). WithOptions(options). WithEventFilter(predicates.ResourceHasFilterLabel(mgr.GetScheme(), predicateLog, r.WatchFilterValue)). Watches( &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(clusterToMachineDeployments), - builder.WithPredicates(predicates.All(mgr.GetScheme(), predicateLog, - predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog), - predicates.ClusterPausedTransitions(mgr.GetScheme(), predicateLog), - )), + predicates.ClusterPausedTransitions(mgr.GetScheme(), predicateLog), // TODO: should this wait for Cluster.Status.InfrastructureReady similar to Infra Machine resources? ).Complete(r) if err != nil { return errors.Wrap(err, "failed setting up with a controller manager") } + r.canUpdateMachineSetCache = cache.New[CanUpdateMachineSetCacheEntry](cache.HookCacheDefaultTTL) r.recorder = mgr.GetEventRecorderFor("machinedeployment-controller") r.ssaCache = ssa.NewCache("machinedeployment") return nil @@ -161,6 +170,17 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (retres ct cluster: cluster, } + // Get machines. + selectorMap, err := metav1.LabelSelectorAsMap(&s.machineDeployment.Spec.Selector) + if err != nil { + return ctrl.Result{}, errors.Wrap(err, "failed to convert label selector to a map") + } + machineList := &clusterv1.MachineList{} + if err := r.Client.List(ctx, machineList, client.InNamespace(s.machineDeployment.Namespace), client.MatchingLabels(selectorMap)); err != nil { + return ctrl.Result{}, errors.Wrap(err, "failed to list Machines") + } + s.machines = collections.FromMachineList(machineList) + defer func() { if err := r.updateStatus(ctx, s); err != nil { reterr = kerrors.NewAggregate([]error{reterr, err}) @@ -175,10 +195,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (retres ct if err := patchMachineDeployment(ctx, patchHelper, deployment, patchOpts...); err != nil { reterr = kerrors.NewAggregate([]error{reterr, err}) } - - if reterr != nil { - retres = ctrl.Result{} - } }() // Handle deletion reconciliation loop. @@ -193,6 +209,7 @@ type scope struct { machineDeployment *clusterv1.MachineDeployment cluster *clusterv1.Cluster machineSets []*clusterv1.MachineSet + machines collections.Machines bootstrapTemplateNotFound bool bootstrapTemplateExists bool infrastructureTemplateNotFound bool @@ -265,6 +282,18 @@ func (r *Reconciler) reconcile(ctx context.Context, s *scope) error { return err } + var anyManagedFieldIssueMitigated bool + for _, ms := range s.machineSets { + managedFieldIssueMitigated, err := ssa.MitigateManagedFieldsIssue(ctx, r.Client, ms, machineDeploymentManagerName) + if err != nil { + return err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + } + if anyManagedFieldIssueMitigated { + return nil // No requeue needed, changes will trigger another reconcile. + } + // If not already present, add a label specifying the MachineDeployment name to MachineSets. // Ensure all required labels exist on the controlled MachineSets. // This logic is needed to add the `cluster.x-k8s.io/deployment-name` label to MachineSets @@ -286,36 +315,155 @@ func (r *Reconciler) reconcile(ctx context.Context, s *scope) error { } } - // Loop over all MachineSets and cleanup managed fields. - // We do this so that MachineSets that were created/patched before (< v1.4.0) the controller adopted - // Server-Side-Apply (SSA) can also work with SSA. Otherwise, fields would be co-owned by our "old" "manager" and - // "capi-machinedeployment" and then we would not be able to e.g. drop labels and annotations. - // Note: We are cleaning up managed fields for all MachineSets, so we're able to remove this code in a few - // Cluster API releases. If we do this only for selected MachineSets, we would have to keep this code forever. - for idx := range s.machineSets { - machineSet := s.machineSets[idx] - if err := ssa.CleanUpManagedFieldsForSSAAdoption(ctx, r.Client, machineSet, machineDeploymentManagerName); err != nil { - return errors.Wrapf(err, "failed to clean up managedFields of MachineSet %s", klog.KObj(machineSet)) - } - } - templateExists := s.infrastructureTemplateExists && (!md.Spec.Template.Spec.Bootstrap.ConfigRef.IsDefined() || s.bootstrapTemplateExists) if ptr.Deref(md.Spec.Paused, false) { - return r.sync(ctx, md, s.machineSets, templateExists) + return r.sync(ctx, md, s.machineSets, s.machines, templateExists) } if md.Spec.Rollout.Strategy.Type == clusterv1.RollingUpdateMachineDeploymentStrategyType { - return r.rolloutRolling(ctx, md, s.machineSets, templateExists) + return r.rolloutRollingUpdate(ctx, md, s.machineSets, s.machines, templateExists) } if md.Spec.Rollout.Strategy.Type == clusterv1.OnDeleteMachineDeploymentStrategyType { - return r.rolloutOnDelete(ctx, md, s.machineSets, templateExists) + return r.rolloutOnDelete(ctx, md, s.machineSets, s.machines, templateExists) } return errors.Errorf("unexpected deployment strategy type: %s", md.Spec.Rollout.Strategy.Type) } +// createOrUpdateMachineSetsAndSyncMachineDeploymentRevision applies changes identified by the rolloutPlanner to both newMS and oldMSs. +// Note: Both newMS and oldMS include the full intent for the SSA apply call with mandatory labels, +// in place propagated fields, the annotations derived from the MachineDeployment, revision annotations +// and also annotations influencing how to perform scale up/down operations. +// scaleIntents instead are handled separately in the rolloutPlanner and should be applied to MachineSets +// before persisting changes. +// Note: When the newMS has been created by the rollout planner, also wait for the cache to be up to date. +func (r *Reconciler) createOrUpdateMachineSetsAndSyncMachineDeploymentRevision(ctx context.Context, p *rolloutPlanner) error { + log := ctrl.LoggerFrom(ctx) + + // Note: newMS goes first so in the logs we will have first create/scale up newMS, then scale down oldMSs + allMSs := append([]*clusterv1.MachineSet{p.newMS}, p.oldMSs...) + + // Get all the diff introduced by the rollout planner. + // Note: collect all the diff first, so for each change we can add an overview of all the MachineSets + // in the MachineDeployment, because those info are required to understand why a change happened. + machineSetsDiff := map[string]machineSetDiff{} + machineSetsSummary := map[string]string{} + for _, ms := range allMSs { + // Update spec.Replicas in the MachineSet. + if scaleIntent, ok := p.scaleIntents[ms.Name]; ok { + ms.Spec.Replicas = ptr.To(scaleIntent) + } + + diff := p.getMachineSetDiff(ms) + machineSetsDiff[ms.Name] = diff + machineSetsSummary[ms.Name] = diff.Summary + } + + // Apply changes to MachineSets. + for _, ms := range allMSs { + log := log.WithValues("MachineSet", klog.KObj(ms)) + ctx := ctrl.LoggerInto(ctx, log) + + // Retrieve the diff for the MachineSet. + // Note: no need to check for diff doesn't exist, all the diff have been computed right above. + diff := machineSetsDiff[ms.Name] + + // Add to the log kv pairs providing the overview of all the MachineSets computed above. + // Note: This value should not be added to the context to prevent propagation to other func. + log = log.WithValues("machineSets", machineSetsSummary) + + if diff.OriginalMS == nil { + // Create the MachineSet. + if err := ssa.Patch(ctx, r.Client, machineDeploymentManagerName, ms); err != nil { + r.recorder.Eventf(p.md, corev1.EventTypeWarning, "FailedCreate", "Failed to create MachineSet %s: %v", klog.KObj(ms), err) + return errors.Wrapf(err, "failed to create MachineSet %s", klog.KObj(ms)) + } + if len(p.oldMSs) > 0 { + log.Info(fmt.Sprintf("MachineSets need rollout: %s", strings.Join(machineSetNames(p.oldMSs), ", ")), "reason", p.createReason) + } + log.Info(fmt.Sprintf("MachineSet %s created, it is now the current MachineSet", ms.Name)) + if diff.DesiredReplicas > 0 { + log.Info(fmt.Sprintf("Scaled up current MachineSet %s from 0 to %d replicas (+%[2]d)", ms.Name, diff.DesiredReplicas)) + } + r.recorder.Eventf(p.md, corev1.EventTypeNormal, "SuccessfulCreate", "Created MachineSet %s with %d replicas", klog.KObj(ms), diff.DesiredReplicas) + + // Keep trying to get the MachineSet. This will force the cache to update and prevent any future reconciliation of + // the MachineDeployment to reconcile with an outdated list of MachineSets which could lead to unwanted creation of + // a duplicate MachineSet. + if err := clientutil.WaitForObjectsToBeAddedToTheCache(ctx, r.Client, "MachineSet creation", ms); err != nil { + return err + } + + continue + } + + // Add to the log kv pairs providing context and details about changes in this MachineSet (reason, diff) + // Note: Those values should not be added to the context to prevent propagation to other func. + statusToLogKeyAndValues := []any{ + "reason", diff.Reason, + "diff", diff.OtherChanges, + } + if len(p.acknowledgedMachineNames) > 0 { + statusToLogKeyAndValues = append(statusToLogKeyAndValues, "acknowledgedMachines", sortAndJoin(p.acknowledgedMachineNames)) + } + if len(p.updatingMachineNames) > 0 { + statusToLogKeyAndValues = append(statusToLogKeyAndValues, "updatingMachines", sortAndJoin(p.updatingMachineNames)) + } + log = log.WithValues(statusToLogKeyAndValues...) + + err := ssa.Patch(ctx, r.Client, machineDeploymentManagerName, ms, ssa.WithCachingProxy{Cache: r.ssaCache, Original: diff.OriginalMS}) + if err != nil { + // Note: If we are Applying a MachineSet with UID set and the MachineSet does not exist anymore, the + // kube-apiserver returns a conflict error. + if (apierrors.IsConflict(err) || apierrors.IsNotFound(err)) && !ms.DeletionTimestamp.IsZero() { + continue + } + r.recorder.Eventf(p.md, corev1.EventTypeWarning, "FailedUpdate", "Failed to update MachineSet %s: %v", klog.KObj(ms), err) + return errors.Wrapf(err, "failed to update MachineSet %s", klog.KObj(ms)) + } + + if diff.DesiredReplicas < diff.OriginalReplicas { + log.Info(fmt.Sprintf("Scaled down %s MachineSet %s from %d to %d replicas (-%d)", diff.Type, ms.Name, diff.OriginalReplicas, diff.DesiredReplicas, diff.OriginalReplicas-diff.DesiredReplicas)) + r.recorder.Eventf(p.md, corev1.EventTypeNormal, "SuccessfulScale", "Scaled down MachineSet %v: %d -> %d", ms.Name, diff.OriginalReplicas, diff.DesiredReplicas) + } + if diff.DesiredReplicas > diff.OriginalReplicas { + log.Info(fmt.Sprintf("Scaled up %s MachineSet %s from %d to %d replicas (+%d)", diff.Type, ms.Name, diff.OriginalReplicas, diff.DesiredReplicas, diff.DesiredReplicas-diff.OriginalReplicas)) + r.recorder.Eventf(p.md, corev1.EventTypeNormal, "SuccessfulScale", "Scaled up MachineSet %v: %d -> %d", ms.Name, diff.OriginalReplicas, diff.DesiredReplicas) + } + if diff.DesiredReplicas == diff.OriginalReplicas && diff.OtherChanges != "" { + log.Info(fmt.Sprintf("Updated %s MachineSet %s", diff.Type, ms.Name)) + } + + // Only wait for cache if the object was changed. + if diff.OriginalMS.ResourceVersion != ms.ResourceVersion { + if err := clientutil.WaitForCacheToBeUpToDate(ctx, r.Client, "MachineSet update", ms); err != nil { + return err + } + } + } + + // Surface the revision annotation on the MD level + if p.md.Annotations == nil { + p.md.Annotations = make(map[string]string) + } + if p.md.Annotations[clusterv1.RevisionAnnotation] != p.revision { + p.md.Annotations[clusterv1.RevisionAnnotation] = p.revision + } + + return nil +} + +func machineSetNames(machineSets []*clusterv1.MachineSet) []string { + names := []string{} + for _, ms := range machineSets { + names = append(names, ms.Name) + } + sort.Strings(names) + return names +} + func (r *Reconciler) reconcileDelete(ctx context.Context, s *scope) error { log := ctrl.LoggerFrom(ctx) if err := r.getAndAdoptMachineSetsForDeployment(ctx, s); err != nil { @@ -332,11 +480,11 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, s *scope) error { for _, ms := range s.machineSets { if ms.DeletionTimestamp.IsZero() { if err := r.Client.Delete(ctx, ms); err != nil && !apierrors.IsNotFound(err) { - return errors.Wrapf(err, "failed to delete MachineSet %s", klog.KObj(ms)) + return errors.Wrapf(err, "failed to delete MachineSet %s (MachineDeployment deleting)", klog.KObj(ms)) } // Note: We intentionally log after Delete because we want this log line to show up only after DeletionTimestamp has been set. // Also, setting DeletionTimestamp doesn't mean the MachineSet is actually deleted (deletion takes some time). - log.Info("Deleting MachineSet (MachineDeployment deleted)", "MachineSet", klog.KObj(ms)) + log.Info(fmt.Sprintf("MachineSet %s deleting (MachineDeployment deleting)", ms.Name), "MachineSet", klog.KObj(ms)) } } @@ -356,22 +504,20 @@ func (r *Reconciler) getAndAdoptMachineSetsForDeployment(ctx context.Context, s return err } + selector, err := metav1.LabelSelectorAsSelector(&md.Spec.Selector) + if err != nil { + return errors.Wrapf(err, "failed to get MachineSets: failed to compute label selector from MachineDeployment.spec.selector") + } + + if selector.Empty() { + return errors.New("failed to get MachineSets: label selector computed from MachineDeployment.spec.selector is empty") + } + filtered := make([]*clusterv1.MachineSet, 0, len(machineSets.Items)) for idx := range machineSets.Items { ms := &machineSets.Items[idx] log := log.WithValues("MachineSet", klog.KObj(ms)) ctx := ctrl.LoggerInto(ctx, log) - selector, err := metav1.LabelSelectorAsSelector(&md.Spec.Selector) - if err != nil { - log.Error(err, "Skipping MachineSet, failed to get label selector from spec selector") - continue - } - - // If a MachineDeployment with a nil or empty selector creeps in, it should match nothing, not everything. - if selector.Empty() { - log.Info("Skipping MachineSet as the selector is empty") - continue - } // Skip this MachineSet unless either selector matches or it has a controller ref pointing to this MachineDeployment if !selector.Matches(labels.Set(ms.Labels)) && !metav1.IsControlledBy(ms, md) { diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rolling.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rolling.go deleted file mode 100644 index d4102185c6..0000000000 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rolling.go +++ /dev/null @@ -1,326 +0,0 @@ -/* -Copyright 2018 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 machinedeployment - -import ( - "context" - "sort" - - "github.com/pkg/errors" - "k8s.io/klog/v2" - "k8s.io/utils/ptr" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" - "sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil" - "sigs.k8s.io/cluster-api/util/patch" -) - -// rolloutRolling implements the logic for rolling a new MachineSet. -func (r *Reconciler) rolloutRolling(ctx context.Context, md *clusterv1.MachineDeployment, msList []*clusterv1.MachineSet, templateExists bool) error { - newMS, oldMSs, err := r.getAllMachineSetsAndSyncRevision(ctx, md, msList, true, templateExists) - if err != nil { - return err - } - - // newMS can be nil in case there is already a MachineSet associated with this deployment, - // but there are only either changes in annotations or MinReadySeconds. Or in other words, - // this can be nil if there are changes, but no replacement of existing machines is needed. - if newMS == nil { - return nil - } - - allMSs := append(oldMSs, newMS) - - // Scale up, if we can. - if err := r.reconcileNewMachineSet(ctx, allMSs, newMS, md); err != nil { - return err - } - - if err := r.syncDeploymentStatus(allMSs, newMS, md); err != nil { - return err - } - - // Scale down, if we can. - if err := r.reconcileOldMachineSets(ctx, allMSs, oldMSs, newMS, md); err != nil { - return err - } - - if err := r.syncDeploymentStatus(allMSs, newMS, md); err != nil { - return err - } - - if mdutil.DeploymentComplete(md, &md.Status) { - if err := r.cleanupDeployment(ctx, oldMSs, md); err != nil { - return err - } - } - - return nil -} - -func (r *Reconciler) reconcileNewMachineSet(ctx context.Context, allMSs []*clusterv1.MachineSet, newMS *clusterv1.MachineSet, deployment *clusterv1.MachineDeployment) error { - if err := r.cleanupDisableMachineCreateAnnotation(ctx, newMS); err != nil { - return err - } - - if deployment.Spec.Replicas == nil { - return errors.Errorf("spec.replicas for MachineDeployment %v is nil, this is unexpected", client.ObjectKeyFromObject(deployment)) - } - - if newMS.Spec.Replicas == nil { - return errors.Errorf("spec.replicas for MachineSet %v is nil, this is unexpected", client.ObjectKeyFromObject(newMS)) - } - - if *(newMS.Spec.Replicas) == *(deployment.Spec.Replicas) { - // Scaling not required. - return nil - } - - if *(newMS.Spec.Replicas) > *(deployment.Spec.Replicas) { - // Scale down. - return r.scaleMachineSet(ctx, newMS, *(deployment.Spec.Replicas), deployment) - } - - newReplicasCount, err := mdutil.NewMSNewReplicas(deployment, allMSs, *newMS.Spec.Replicas) - if err != nil { - return err - } - return r.scaleMachineSet(ctx, newMS, newReplicasCount, deployment) -} - -func (r *Reconciler) reconcileOldMachineSets(ctx context.Context, allMSs []*clusterv1.MachineSet, oldMSs []*clusterv1.MachineSet, newMS *clusterv1.MachineSet, deployment *clusterv1.MachineDeployment) error { - log := ctrl.LoggerFrom(ctx) - - if deployment.Spec.Replicas == nil { - return errors.Errorf("spec.replicas for MachineDeployment %v is nil, this is unexpected", - client.ObjectKeyFromObject(deployment)) - } - - if newMS.Spec.Replicas == nil { - return errors.Errorf("spec.replicas for MachineSet %v is nil, this is unexpected", - client.ObjectKeyFromObject(newMS)) - } - - oldMachinesCount := mdutil.GetReplicaCountForMachineSets(oldMSs) - if oldMachinesCount == 0 { - // Can't scale down further - return nil - } - - allMachinesCount := mdutil.GetReplicaCountForMachineSets(allMSs) - log.V(4).Info("New MachineSet has available machines", - "machineset", client.ObjectKeyFromObject(newMS).String(), "available-replicas", ptr.Deref(newMS.Status.AvailableReplicas, 0)) - maxUnavailable := mdutil.MaxUnavailable(*deployment) - - // Check if we can scale down. We can scale down in the following 2 cases: - // * Some old MachineSets have unhealthy replicas, we could safely scale down those unhealthy replicas since that won't further - // increase unavailability. - // * New MachineSet has scaled up and it's replicas becomes ready, then we can scale down old MachineSets in a further step. - // - // maxScaledDown := allMachinesCount - minAvailable - newMachineSetMachinesUnavailable - // take into account not only maxUnavailable and any surge machines that have been created, but also unavailable machines from - // the newMS, so that the unavailable machines from the newMS would not make us scale down old MachineSets in a further - // step(that will increase unavailability). - // - // Concrete example: - // - // * 10 replicas - // * 2 maxUnavailable (absolute number, not percent) - // * 3 maxSurge (absolute number, not percent) - // - // case 1: - // * Deployment is updated, newMS is created with 3 replicas, oldMS is scaled down to 8, and newMS is scaled up to 5. - // * The new MachineSet machines crashloop and never become available. - // * allMachinesCount is 13. minAvailable is 8. newMSMachinesUnavailable is 5. - // * A node fails and causes one of the oldMS machines to become unavailable. However, 13 - 8 - 5 = 0, so the oldMS won't be scaled down. - // * The user notices the crashloop and does kubectl rollout undo to rollback. - // * newMSMachinesUnavailable is 1, since we rolled back to the good MachineSet, so maxScaledDown = 13 - 8 - 1 = 4. 4 of the crashlooping machines will be scaled down. - // * The total number of machines will then be 9 and the newMS can be scaled up to 10. - // - // case 2: - // Same example, but pushing a new machine template instead of rolling back (aka "roll over"): - // * The new MachineSet created must start with 0 replicas because allMachinesCount is already at 13. - // * However, newMSMachinesUnavailable would also be 0, so the 2 old MachineSets could be scaled down by 5 (13 - 8 - 0), which would then - // allow the new MachineSet to be scaled up by 5. - availableReplicas := ptr.Deref(newMS.Status.AvailableReplicas, 0) - - minAvailable := *(deployment.Spec.Replicas) - maxUnavailable - newMSUnavailableMachineCount := *(newMS.Spec.Replicas) - availableReplicas - maxScaledDown := allMachinesCount - minAvailable - newMSUnavailableMachineCount - if maxScaledDown <= 0 { - return nil - } - - // Clean up unhealthy replicas first, otherwise unhealthy replicas will block deployment - // and cause timeout. See https://github.com/kubernetes/kubernetes/issues/16737 - oldMSs, cleanupCount, err := r.cleanupUnhealthyReplicas(ctx, oldMSs, deployment, maxScaledDown) - if err != nil { - return err - } - - log.V(4).Info("Cleaned up unhealthy replicas from old MachineSets", "count", cleanupCount) - - // Scale down old MachineSets, need check maxUnavailable to ensure we can scale down - allMSs = oldMSs - allMSs = append(allMSs, newMS) - scaledDownCount, err := r.scaleDownOldMachineSetsForRollingUpdate(ctx, allMSs, oldMSs, deployment) - if err != nil { - return err - } - - log.V(4).Info("Scaled down old MachineSets of MachineDeployment", "count", scaledDownCount) - return nil -} - -// cleanupUnhealthyReplicas will scale down old MachineSets with unhealthy replicas, so that all unhealthy replicas will be deleted. -func (r *Reconciler) cleanupUnhealthyReplicas(ctx context.Context, oldMSs []*clusterv1.MachineSet, deployment *clusterv1.MachineDeployment, maxCleanupCount int32) ([]*clusterv1.MachineSet, int32, error) { - log := ctrl.LoggerFrom(ctx) - - sort.Sort(mdutil.MachineSetsByCreationTimestamp(oldMSs)) - - // Scale down all old MachineSets with any unhealthy replicas. MachineSet will honour spec.deletion.order - // for deleting Machines. Machines with a deletion timestamp, with a failure message or without a nodeRef - // are preferred for all strategies. - // This results in a best effort to remove machines backing unhealthy nodes. - totalScaledDown := int32(0) - - for _, targetMS := range oldMSs { - if targetMS.Spec.Replicas == nil { - return nil, 0, errors.Errorf("spec.replicas for MachineSet %v is nil, this is unexpected", client.ObjectKeyFromObject(targetMS)) - } - - if totalScaledDown >= maxCleanupCount { - break - } - - oldMSReplicas := *(targetMS.Spec.Replicas) - if oldMSReplicas == 0 { - // cannot scale down this MachineSet. - continue - } - - oldMSAvailableReplicas := ptr.Deref(targetMS.Status.AvailableReplicas, 0) - log.V(4).Info("Found available Machines in old MachineSet", - "count", oldMSAvailableReplicas, "target-machineset", client.ObjectKeyFromObject(targetMS).String()) - if oldMSReplicas == oldMSAvailableReplicas { - // no unhealthy replicas found, no scaling required. - continue - } - - remainingCleanupCount := maxCleanupCount - totalScaledDown - unhealthyCount := oldMSReplicas - oldMSAvailableReplicas - scaledDownCount := min(remainingCleanupCount, unhealthyCount) - newReplicasCount := oldMSReplicas - scaledDownCount - - if newReplicasCount > oldMSReplicas { - return nil, 0, errors.Errorf("when cleaning up unhealthy replicas, got invalid request to scale down %v: %d -> %d", - client.ObjectKeyFromObject(targetMS), oldMSReplicas, newReplicasCount) - } - - if err := r.scaleMachineSet(ctx, targetMS, newReplicasCount, deployment); err != nil { - return nil, totalScaledDown, err - } - - totalScaledDown += scaledDownCount - } - - return oldMSs, totalScaledDown, nil -} - -// scaleDownOldMachineSetsForRollingUpdate scales down old MachineSets when deployment strategy is "RollingUpdate". -// Need check maxUnavailable to ensure availability. -func (r *Reconciler) scaleDownOldMachineSetsForRollingUpdate(ctx context.Context, allMSs []*clusterv1.MachineSet, oldMSs []*clusterv1.MachineSet, deployment *clusterv1.MachineDeployment) (int32, error) { - log := ctrl.LoggerFrom(ctx) - - if deployment.Spec.Replicas == nil { - return 0, errors.Errorf("spec.replicas for MachineDeployment %v is nil, this is unexpected", client.ObjectKeyFromObject(deployment)) - } - - maxUnavailable := mdutil.MaxUnavailable(*deployment) - minAvailable := *(deployment.Spec.Replicas) - maxUnavailable - - // Find the number of available machines. - availableMachineCount := ptr.Deref(mdutil.GetAvailableReplicaCountForMachineSets(allMSs), 0) - - // Check if we can scale down. - if availableMachineCount <= minAvailable { - // Cannot scale down. - return 0, nil - } - - log.V(4).Info("Found available machines in deployment, scaling down old MSes", "count", availableMachineCount) - - sort.Sort(mdutil.MachineSetsByCreationTimestamp(oldMSs)) - - totalScaledDown := int32(0) - totalScaleDownCount := availableMachineCount - minAvailable - for _, targetMS := range oldMSs { - if targetMS.Spec.Replicas == nil { - return 0, errors.Errorf("spec.replicas for MachineSet %v is nil, this is unexpected", client.ObjectKeyFromObject(targetMS)) - } - - if totalScaledDown >= totalScaleDownCount { - // No further scaling required. - break - } - - if *(targetMS.Spec.Replicas) == 0 { - // cannot scale down this MachineSet. - continue - } - - // Scale down. - scaleDownCount := min(*(targetMS.Spec.Replicas), totalScaleDownCount-totalScaledDown) - newReplicasCount := *(targetMS.Spec.Replicas) - scaleDownCount - if newReplicasCount > *(targetMS.Spec.Replicas) { - return totalScaledDown, errors.Errorf("when scaling down old MachineSet, got invalid request to scale down %v: %d -> %d", - client.ObjectKeyFromObject(targetMS), *(targetMS.Spec.Replicas), newReplicasCount) - } - - if err := r.scaleMachineSet(ctx, targetMS, newReplicasCount, deployment); err != nil { - return totalScaledDown, err - } - - totalScaledDown += scaleDownCount - } - - return totalScaledDown, nil -} - -// cleanupDisableMachineCreateAnnotation will remove the disable machine create annotation from new MachineSets that were created during reconcileOldMachineSetsOnDelete. -func (r *Reconciler) cleanupDisableMachineCreateAnnotation(ctx context.Context, newMS *clusterv1.MachineSet) error { - log := ctrl.LoggerFrom(ctx, "MachineSet", klog.KObj(newMS)) - - if newMS.Annotations != nil { - if _, ok := newMS.Annotations[clusterv1.DisableMachineCreateAnnotation]; ok { - log.V(4).Info("removing annotation on latest MachineSet to enable machine creation") - patchHelper, err := patch.NewHelper(newMS, r.Client) - if err != nil { - return err - } - delete(newMS.Annotations, clusterv1.DisableMachineCreateAnnotation) - err = patchHelper.Patch(ctx, newMS) - if err != nil { - return err - } - } - } - - return nil -} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rollout_ondelete.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rollout_ondelete.go index ce485797ee..698748f0e5 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rollout_ondelete.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rollout_ondelete.go @@ -19,47 +19,35 @@ package machinedeployment import ( "context" "fmt" + "sort" - "github.com/pkg/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/klog/v2" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil" - "sigs.k8s.io/cluster-api/util/patch" + "sigs.k8s.io/cluster-api/util/collections" ) -// rolloutOnDelete implements the logic for the OnDelete rollout strategy. -func (r *Reconciler) rolloutOnDelete(ctx context.Context, md *clusterv1.MachineDeployment, msList []*clusterv1.MachineSet, templateExists bool) error { - newMS, oldMSs, err := r.getAllMachineSetsAndSyncRevision(ctx, md, msList, true, templateExists) - if err != nil { +// rolloutOnDelete reconcile machine sets controlled by a MachineDeployment that is using the OnDelete strategy. +func (r *Reconciler) rolloutOnDelete(ctx context.Context, md *clusterv1.MachineDeployment, msList []*clusterv1.MachineSet, machines collections.Machines, templateExists bool) error { + planner := newRolloutPlanner(r.Client, r.RuntimeClient, r.canUpdateMachineSetCache) + if err := planner.init(ctx, md, msList, machines.UnsortedList(), true, templateExists); err != nil { return err } - // newMS can be nil in case there is already a MachineSet associated with this deployment, - // but there are only either changes in annotations or MinReadySeconds. Or in other words, - // this can be nil if there are changes, but no replacement of existing machines is needed. - if newMS == nil { - return nil - } - - allMSs := append(oldMSs, newMS) - - // Scale up, if we can. - if err := r.reconcileNewMachineSetOnDelete(ctx, allMSs, newMS, md); err != nil { + if err := planner.planOnDelete(ctx); err != nil { return err } - if err := r.syncDeploymentStatus(allMSs, newMS, md); err != nil { + if err := r.createOrUpdateMachineSetsAndSyncMachineDeploymentRevision(ctx, planner); err != nil { return err } - // Scale down, if we can. - if err := r.reconcileOldMachineSetsOnDelete(ctx, oldMSs, allMSs, md); err != nil { - return err - } + newMS := planner.newMS + oldMSs := planner.oldMSs + allMSs := append(oldMSs, newMS) if err := r.syncDeploymentStatus(allMSs, newMS, md); err != nil { return err @@ -74,100 +62,88 @@ func (r *Reconciler) rolloutOnDelete(ctx context.Context, md *clusterv1.MachineD return nil } +// planOnDelete determine how to proceed with the rollout when using the OnDelete strategy if we are not yet at the desired state. +func (p *rolloutPlanner) planOnDelete(ctx context.Context) error { + // Scale up, if we can. + if err := p.reconcileNewMachineSet(ctx); err != nil { + return err + } + + // Scale down, if we can. + p.reconcileOldMachineSetsOnDelete(ctx) + return nil +} + // reconcileOldMachineSetsOnDelete handles reconciliation of Old MachineSets associated with the MachineDeployment in the OnDelete rollout strategy. -func (r *Reconciler) reconcileOldMachineSetsOnDelete(ctx context.Context, oldMSs []*clusterv1.MachineSet, allMSs []*clusterv1.MachineSet, deployment *clusterv1.MachineDeployment) error { +func (p *rolloutPlanner) reconcileOldMachineSetsOnDelete(ctx context.Context) { log := ctrl.LoggerFrom(ctx) - if deployment.Spec.Replicas == nil { - return errors.Errorf("spec replicas for MachineDeployment %q/%q is nil, this is unexpected", - deployment.Namespace, deployment.Name) + oldMachinesCount := mdutil.GetReplicaCountForMachineSets(p.oldMSs) + if oldMachinesCount == 0 { + // Can't scale down further + return } - log.V(4).Info("Checking to see if machines have been deleted or are in the process of deleting for old machine sets") - totalReplicas := mdutil.GetReplicaCountForMachineSets(allMSs) - scaleDownAmount := totalReplicas - *deployment.Spec.Replicas - for _, oldMS := range oldMSs { - log := log.WithValues("MachineSet", klog.KObj(oldMS)) - if oldMS.Spec.Replicas == nil || *oldMS.Spec.Replicas <= 0 { - log.V(4).Info("fully scaled down") - continue - } - if oldMS.Annotations == nil { - oldMS.Annotations = map[string]string{} - } - if _, ok := oldMS.Annotations[clusterv1.DisableMachineCreateAnnotation]; !ok { - log.V(4).Info("setting annotation on old MachineSet to disable machine creation") - patchHelper, err := patch.NewHelper(oldMS, r.Client) - if err != nil { - return err - } - oldMS.Annotations[clusterv1.DisableMachineCreateAnnotation] = "true" - if err := patchHelper.Patch(ctx, oldMS); err != nil { - return err - } - } - selectorMap, err := metav1.LabelSelectorAsMap(&oldMS.Spec.Selector) - if err != nil { - log.V(4).Info("Failed to convert MachineSet label selector to a map", "err", err) + + // Determine if there are more Machines than MD.spec.replicas, e.g. due to a scale down in MD. + newMSReplicas := ptr.Deref(p.newMS.Spec.Replicas, 0) + if v, ok := p.scaleIntents[p.newMS.Name]; ok { + newMSReplicas = v + } + totReplicas := oldMachinesCount + newMSReplicas + totalScaleDownCount := max(totReplicas-ptr.Deref(p.md.Spec.Replicas, 0), 0) + + // Sort oldMSs so the system will start deleting from the oldest MS first. + sort.Sort(mdutil.MachineSetsByCreationTimestamp(p.oldMSs)) + + // Start scaling down old machine sets to acknowledge spec.replicas without corresponding status.replicas. + // Note: spec.replicas without corresponding status.replicas exists + // - after a user manually deletes a replica + // - when a newMS not yet fully provisioned suddenly becomes an oldMS. + // In both cases spec.replicas without corresponding status.replicas should be dropped, no matter + // if there are replicas to be scaled down due to a scale down in MD or not. + // However, just in case there are replicas to be scaled down due to a scale down in MD, deleted replicas should + // be deducted from the totalScaleDownCount. + for _, oldMS := range p.oldMSs { + // No op if this MS has been already scaled down to zero. + if ptr.Deref(oldMS.Spec.Replicas, 0) <= 0 { continue } - log.V(4).Info("Fetching Machines associated with MachineSet") - // Get all Machines linked to this MachineSet. - allMachinesInOldMS := &clusterv1.MachineList{} - if err := r.Client.List(ctx, - allMachinesInOldMS, - client.InNamespace(oldMS.Namespace), - client.MatchingLabels(selectorMap), - ); err != nil { - return errors.Wrap(err, "failed to list machines") - } - totalMachineCount := int32(len(allMachinesInOldMS.Items)) - log.V(4).Info("Retrieved machines", "totalMachineCount", totalMachineCount) - updatedReplicaCount := totalMachineCount - mdutil.GetDeletingMachineCount(allMachinesInOldMS) - if updatedReplicaCount < 0 { - return errors.Errorf("negative updated replica count %d for MachineSet %q, this is unexpected", updatedReplicaCount, oldMS.Name) - } - machineSetScaleDownAmountDueToMachineDeletion := *oldMS.Spec.Replicas - updatedReplicaCount - if machineSetScaleDownAmountDueToMachineDeletion < 0 { - log.V(4).Info(fmt.Sprintf("Error reconciling MachineSet %s", oldMS.Name), "err", errors.Errorf("Unexpected negative scale down amount: %d", machineSetScaleDownAmountDueToMachineDeletion)) - } - scaleDownAmount -= machineSetScaleDownAmountDueToMachineDeletion - log.V(4).Info("Adjusting replica count for deleted machines", "oldReplicas", oldMS.Spec.Replicas, "newReplicas", updatedReplicaCount) - log.V(4).Info("Scaling down", "replicas", updatedReplicaCount) - if err := r.scaleMachineSet(ctx, oldMS, updatedReplicaCount, deployment); err != nil { - return err + + scaleDownCount := max(ptr.Deref(oldMS.Spec.Replicas, 0)-ptr.Deref(oldMS.Status.Replicas, 0), 0) + if scaleDownCount > 0 { + newScaleIntent := max(ptr.Deref(oldMS.Spec.Replicas, 0)-scaleDownCount, 0) + p.addNotef(oldMS, "scale down to align to existing Machines") + log.V(5).Info(fmt.Sprintf("Setting scale down intent for MachineSet %s to %d replicas (-%d)", oldMS.Name, newScaleIntent, scaleDownCount), "MachineSet", klog.KObj(oldMS)) + p.scaleIntents[oldMS.Name] = newScaleIntent + + totalScaleDownCount -= scaleDownCount } } - log.V(4).Info("Finished reconcile of Old MachineSets to account for deleted machines. Now analyzing if there's more potential to scale down") - for _, oldMS := range oldMSs { - log := log.WithValues("MachineSet", klog.KObj(oldMS)) - if scaleDownAmount <= 0 { + + // Scale down additional replicas if replicas removed in the for loop above were not enough to align to MD replicas. + for _, oldMS := range p.oldMSs { + // No op if there is no scaling down left. + if totalScaleDownCount <= 0 { break } - if oldMS.Spec.Replicas == nil || *oldMS.Spec.Replicas <= 0 { - log.V(4).Info("Fully scaled down") - continue - } - updatedReplicaCount := *oldMS.Spec.Replicas - if updatedReplicaCount >= scaleDownAmount { - updatedReplicaCount -= scaleDownAmount - scaleDownAmount = 0 - } else { - scaleDownAmount -= updatedReplicaCount - updatedReplicaCount = 0 + + // No op if this MS has been already scaled down to zero. + scaleIntent := ptr.Deref(oldMS.Spec.Replicas, 0) + if v, ok := p.scaleIntents[oldMS.Name]; ok { + scaleIntent = v } - log.V(4).Info("Scaling down", "replicas", updatedReplicaCount) - if err := r.scaleMachineSet(ctx, oldMS, updatedReplicaCount, deployment); err != nil { - return err + + if scaleIntent <= 0 { + continue } - } - log.V(4).Info("Finished reconcile of all old MachineSets") - return nil -} -// reconcileNewMachineSetOnDelete handles reconciliation of the latest MachineSet associated with the MachineDeployment in the OnDelete rollout strategy. -func (r *Reconciler) reconcileNewMachineSetOnDelete(ctx context.Context, allMSs []*clusterv1.MachineSet, newMS *clusterv1.MachineSet, deployment *clusterv1.MachineDeployment) error { - if err := r.cleanupDisableMachineCreateAnnotation(ctx, newMS); err != nil { - return err - } + scaleDownCount := min(scaleIntent, totalScaleDownCount) + if scaleDownCount > 0 { + newScaleIntent := max(ptr.Deref(oldMS.Spec.Replicas, 0)-scaleDownCount, 0) + p.addNotef(oldMS, "scale down to align MachineSet spec.replicas to MachineDeployment spec.replicas") + log.V(5).Info(fmt.Sprintf("Setting scale down intent for MachineSet %s to %d replicas (-%d)", oldMS.Name, newScaleIntent, scaleDownCount), "MachineSet", klog.KObj(oldMS)) + p.scaleIntents[oldMS.Name] = newScaleIntent - return r.reconcileNewMachineSet(ctx, allMSs, newMS, deployment) + totalScaleDownCount -= scaleDownCount + } + } } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rollout_planner.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rollout_planner.go new file mode 100644 index 0000000000..214752ad69 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rollout_planner.go @@ -0,0 +1,449 @@ +/* +Copyright 2018 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 machinedeployment + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" + + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" + "sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil" + "sigs.k8s.io/cluster-api/internal/util/hash" + "sigs.k8s.io/cluster-api/util/annotations" + "sigs.k8s.io/cluster-api/util/cache" +) + +type rolloutPlanner struct { + Client client.Client + RuntimeClient runtimeclient.Client + canUpdateMachineSetCache cache.Cache[CanUpdateMachineSetCacheEntry] + + md *clusterv1.MachineDeployment + revision string + + originalMSs map[string]*clusterv1.MachineSet + machines []*clusterv1.Machine + acknowledgedMachineNames []string + updatingMachineNames []string + + newMS *clusterv1.MachineSet + createReason string + + oldMSs []*clusterv1.MachineSet + upToDateResults map[string]mdutil.UpToDateResult + + scaleIntents map[string]int32 + notes map[string][]string + + overrideComputeDesiredMS func(ctx context.Context, deployment *clusterv1.MachineDeployment, currentMS *clusterv1.MachineSet) (*clusterv1.MachineSet, error) + overrideCanUpdateMachineSetInPlace func(ctx context.Context, oldMS, newMS *clusterv1.MachineSet) (bool, error) + overrideCanExtensionsUpdateMachineSet func(ctx context.Context, oldMS, newMS *clusterv1.MachineSet, templateObjects *templateObjects, extensionHandlers []string) (bool, []string, error) +} + +func newRolloutPlanner(c client.Client, runtimeClient runtimeclient.Client, canUpdateMachineSetCache cache.Cache[CanUpdateMachineSetCacheEntry]) *rolloutPlanner { + return &rolloutPlanner{ + Client: c, + RuntimeClient: runtimeClient, + canUpdateMachineSetCache: canUpdateMachineSetCache, + scaleIntents: make(map[string]int32), + notes: make(map[string][]string), + } +} + +// init rollout planner internal state by taking care of: +// - Identifying newMS and oldMSs +// - Create the newMS if it not exists +// - Compute the initial version of desired state for newMS and oldMSs with mandatory labels, in place propagated fields +// and the annotations derived from the MachineDeployment. +// +// Note: rollout planner might change desired state later on in the planning phase, e.g. add/remove annotations influencing +// how to perform scale up/down operations. +func (p *rolloutPlanner) init(ctx context.Context, md *clusterv1.MachineDeployment, msList []*clusterv1.MachineSet, machines []*clusterv1.Machine, createNewMSIfNotExist bool, mdTemplateExists bool) error { + if md == nil { + return errors.New("machineDeployment is nil, this is unexpected") + } + + if md.Spec.Replicas == nil { + return errors.Errorf("spec.replicas for MachineDeployment %v is nil, this is unexpected", client.ObjectKeyFromObject(p.md)) + } + + for _, ms := range msList { + if ms.Spec.Replicas == nil { + return errors.Errorf("spec.replicas for MachineSet %v is nil, this is unexpected", client.ObjectKeyFromObject(ms)) + } + } + + // Store md and machines. + p.md = md + p.machines = machines + + // Store original MS, for usage later with SSA patches / SSA caching. + p.originalMSs = make(map[string]*clusterv1.MachineSet) + for _, ms := range msList { + p.originalMSs[ms.Name] = ms.DeepCopy() + } + + // Try to find a MachineSet which matches the MachineDeployments intent, the newMS; consider all the other MachineSets as oldMs. + // NOTE: Fields propagated in-place from the MD are not considered by the comparison, they are not relevant for the rollout decision. + // NOTE: Expiration of MD rolloutAfter is relevant for the rollout decision, and thus it is considered in FindNewAndOldMachineSets. + currentNewMS, currentOldMSs, upToDateResults, createReason := mdutil.FindNewAndOldMachineSets(md, msList, metav1.Now()) + p.upToDateResults = upToDateResults + + // Compute desired state for the old MS, with mandatory labels, fields in-place propagated from the MachineDeployment etc. + for _, currentOldMS := range currentOldMSs { + desiredOldMS, err := p.computeDesiredOldMS(ctx, currentOldMS) + if err != nil { + return err + } + p.oldMSs = append(p.oldMSs, desiredOldMS) + } + + // If there is a current NewMS, compute the desired state for it with mandatory labels, fields in-place propagated from the MachineDeployment etc. + if currentNewMS != nil { + oldRevision := currentNewMS.Annotations[clusterv1.RevisionAnnotation] + desiredNewMS, err := p.computeDesiredNewMS(ctx, currentNewMS) + if err != nil { + return err + } + p.newMS = desiredNewMS + // Note: when an oldMS becomes again the current one, computeDesiredNewMS changes its revision number; + // the new revision number is also surfaced in p.revision, and thus we are using this information + // to determine when to add this note. + if oldRevision != p.revision { + p.addNotef(p.newMS, "this is now the current MachineSet") + } + return nil + } + + // If there is no current NewMS, create one if required and possible. + if !createNewMSIfNotExist { + return nil + } + + if !mdTemplateExists { + return errors.New("cannot create a MachineSet when templates do not exist") + } + + // Compute a new MachineSet with mandatory labels, fields in-place propagated from the MachineDeployment etc. + desiredNewMS, err := p.computeDesiredNewMS(ctx, nil) + if err != nil { + return err + } + p.newMS = desiredNewMS + p.createReason = createReason + return nil +} + +// computeDesiredNewMS with mandatory labels, in place propagated fields and the annotations derived from the MachineDeployment. +// Additionally, this procedure ensure the annotations tracking revisions numbers on the newMS is upToDate. +// Note: because we are using Server-Side-Apply we always have to calculate the full object. +func (p *rolloutPlanner) computeDesiredNewMS(ctx context.Context, currentNewMS *clusterv1.MachineSet) (*clusterv1.MachineSet, error) { + computeFunc := computeDesiredMS + if p.overrideComputeDesiredMS != nil { + computeFunc = p.overrideComputeDesiredMS + } + desiredNewMS, err := computeFunc(ctx, p.md, currentNewMS) + if err != nil { + return nil, err + } + + // For newMS, make sure the revision annotation has the highest revision number across all MS + update the revision history annotation accordingly. + revisionAnnotations, revision, err := mdutil.ComputeRevisionAnnotations(ctx, currentNewMS, p.oldMSs) + if err != nil { + return nil, err + } + annotations.AddAnnotations(desiredNewMS, revisionAnnotations) + p.revision = revision + + // Always allow creation of machines on newMS. + desiredNewMS.Annotations[clusterv1.DisableMachineCreateAnnotation] = "false" + return desiredNewMS, nil +} + +// computeDesiredOldMS with mandatory labels, in place propagated fields and the annotations derived from the MachineDeployment. +// Additionally, this procedure ensure the annotations tracking revisions numbers are carried over. +// Note: because we are using Server-Side-Apply we always have to calculate the full object. +func (p *rolloutPlanner) computeDesiredOldMS(ctx context.Context, currentOldMS *clusterv1.MachineSet) (*clusterv1.MachineSet, error) { + computeFunc := computeDesiredMS + if p.overrideComputeDesiredMS != nil { + computeFunc = p.overrideComputeDesiredMS + } + desiredOldMS, err := computeFunc(ctx, p.md, currentOldMS) + if err != nil { + return nil, err + } + + // For oldMS, carry over the revision annotations (those annotations should not be updated for oldMSs). + revisionAnnotations := mdutil.GetRevisionAnnotations(ctx, currentOldMS) + annotations.AddAnnotations(desiredOldMS, revisionAnnotations) + + // Disable creation of machines on oldMS when rollout strategy is on delete. + if desiredOldMS.Annotations == nil { + desiredOldMS.Annotations = map[string]string{} + } + if p.md.Spec.Rollout.Strategy.Type == clusterv1.OnDeleteMachineDeploymentStrategyType { + desiredOldMS.Annotations[clusterv1.DisableMachineCreateAnnotation] = "true" + } else { + desiredOldMS.Annotations[clusterv1.DisableMachineCreateAnnotation] = "false" + } + return desiredOldMS, nil +} + +// computeDesiredMS computes the desired MachineSet, which could be either a newly created newMS, or the new desired version of an existing newMS/OldMS. +// Note: because we are using Server-Side-Apply we always have to calculate the full object. +func computeDesiredMS(ctx context.Context, deployment *clusterv1.MachineDeployment, currentMS *clusterv1.MachineSet) (*clusterv1.MachineSet, error) { + var name string + var uid types.UID + var finalizers []string + var uniqueIdentifierLabelValue string + var machineTemplateSpec clusterv1.MachineSpec + var status clusterv1.MachineSetStatus + var replicas int32 + var creationTimestamp metav1.Time + + if currentMS == nil { + // For a new MachineSet: compute a new uniqueIdentifier, a new MachineSet name, finalizers, replicas and machine template spec (take the one from MachineDeployment) + // Note: Replicas count might be updated by the rollout planner later in the same reconcile or in following reconcile. + + // Note: In previous Cluster API versions (< v1.4.0), the label value was the hash of the full machine + // template. Since the introduction of in-place mutation we are ignoring all in-place mutable fields, + // and using it as a info to be used for building a unique label selector. Instead, the rollout decision + // is not using the hash anymore. + templateHash, err := hash.Compute(mdutil.MachineTemplateDeepCopyRolloutFields(&deployment.Spec.Template)) + if err != nil { + return nil, errors.Wrap(err, "failed to compute desired MachineSet: failed to compute machine template hash") + } + // Append a random string at the end of template hash. This is required to distinguish MachineSets that + // could be created with the same spec as a result of rolloutAfter. + var randomSuffix string + name, randomSuffix = computeNewMachineSetName(deployment.Name + "-") + uniqueIdentifierLabelValue = fmt.Sprintf("%d-%s", templateHash, randomSuffix) + replicas = 0 + machineTemplateSpec = *deployment.Spec.Template.Spec.DeepCopy() + creationTimestamp = metav1.NewTime(time.Now()) + } else { + // For updating an existing MachineSet use name, uid, finalizers, replicas, uniqueIdentifier and machine template spec from existingMS. + // Note: We use the uid, to ensure that the Server-Side-Apply only updates the existingMS. + // Note: Replicas count might be updated by the rollout planner later in the same reconcile or in following reconcile. + var uniqueIdentifierLabelExists bool + uniqueIdentifierLabelValue, uniqueIdentifierLabelExists = currentMS.Labels[clusterv1.MachineDeploymentUniqueLabel] + if !uniqueIdentifierLabelExists { + return nil, errors.Errorf("failed to compute desired MachineSet: failed to get unique identifier from %q annotation", + clusterv1.MachineDeploymentUniqueLabel) + } + + name = currentMS.Name + uid = currentMS.UID + // Preserve all existing finalizers (including foregroundDeletion finalizer). + finalizers = currentMS.Finalizers + replicas = *currentMS.Spec.Replicas + machineTemplateSpec = *currentMS.Spec.Template.Spec.DeepCopy() + status = currentMS.Status + creationTimestamp = currentMS.CreationTimestamp + } + + // Construct the basic MachineSet. + desiredMS := &clusterv1.MachineSet{ + TypeMeta: metav1.TypeMeta{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "MachineSet", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: deployment.Namespace, + // NOTE: Carry over creationTimestamp from current MS, because it is required by the sorting functions + // used in the planning phase, e.g. MachineSetsByCreationTimestamp. + // NOTE: For newMS, this value is set to now, but it will be overridden when actual creation happens + // NOTE: CreationTimestamp will be dropped from the SSA intent by the SSA helper. + CreationTimestamp: creationTimestamp, + // Note: By setting the ownerRef on creation we signal to the MachineSet controller that this is not a stand-alone MachineSet. + OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(deployment, machineDeploymentKind)}, + UID: uid, + Finalizers: finalizers, + }, + Spec: clusterv1.MachineSetSpec{ + Replicas: &replicas, + ClusterName: deployment.Spec.ClusterName, + Template: clusterv1.MachineTemplateSpec{ + Spec: machineTemplateSpec, + }, + }, + // NOTE: Carry over status from current MS, because it is required by mdutil functions + // used in the planning phase, e.g. GetAvailableReplicaCountForMachineSets. + // NOTE: Status will be dropped from the SSA intent by the SSA helper. + Status: status, + } + + // Set the in-place mutable fields. + // When we create a new MachineSet we will just create the MachineSet with those fields. + // When we update an existing MachineSet will we update the fields on the existing MachineSet (in-place mutate). + + // Set labels and .spec.template.labels. + desiredMS.Labels = mdutil.CloneAndAddLabel(deployment.Spec.Template.Labels, + clusterv1.MachineDeploymentUniqueLabel, uniqueIdentifierLabelValue) + + // Always set the MachineDeploymentNameLabel. + // Note: If a client tries to create a MachineDeployment without a selector, the MachineDeployment webhook + // will add this label automatically. But we want this label to always be present even if the MachineDeployment + // has a selector which doesn't include it. Therefore, we have to set it here explicitly. + desiredMS.Labels[clusterv1.MachineDeploymentNameLabel] = deployment.Name + desiredMS.Spec.Template.Labels = mdutil.CloneAndAddLabel(deployment.Spec.Template.Labels, + clusterv1.MachineDeploymentUniqueLabel, uniqueIdentifierLabelValue) + + // Set selector. + desiredMS.Spec.Selector = *mdutil.CloneSelectorAndAddLabel(&deployment.Spec.Selector, clusterv1.MachineDeploymentUniqueLabel, uniqueIdentifierLabelValue) + + // Set annotations and .spec.template.annotations. + // Note: Additional annotations might be added by the rollout planner later in the same reconcile. + // Note: Intentionally, we are not setting the following labels: + // - clusterv1.RevisionAnnotation + the deprecated revisionHistoryAnnotation + // - for newMS, we should always keep those annotations upToDate + // - for oldMS, we should carry over those annotations from previous reconcile + // - clusterv1.DisableMachineCreateAnnotation + // - it should be set to true only on oldMS and if strategy is on delete, otherwise set to false. + desiredMS.Annotations = mdutil.MachineSetAnnotationsFromMachineDeployment(ctx, deployment) + desiredMS.Spec.Template.Annotations = cloneStringMap(deployment.Spec.Template.Annotations) + + // Set all other in-place mutable fields. + desiredMS.Spec.Deletion.Order = deployment.Spec.Deletion.Order + desiredMS.Spec.MachineNaming = deployment.Spec.MachineNaming + desiredMS.Spec.Template.Spec.MinReadySeconds = deployment.Spec.Template.Spec.MinReadySeconds + desiredMS.Spec.Template.Spec.ReadinessGates = deployment.Spec.Template.Spec.ReadinessGates + desiredMS.Spec.Template.Spec.Deletion.NodeDrainTimeoutSeconds = deployment.Spec.Template.Spec.Deletion.NodeDrainTimeoutSeconds + desiredMS.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds = deployment.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds + desiredMS.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = deployment.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds + desiredMS.Spec.Template.Spec.Taints = deployment.Spec.Template.Spec.Taints + + return desiredMS, nil +} + +func (p *rolloutPlanner) addNotef(ms *clusterv1.MachineSet, format string, a ...any) { + msg := fmt.Sprintf(format, a...) + for _, note := range p.notes[ms.Name] { + if note == msg { + return + } + } + p.notes[ms.Name] = append(p.notes[ms.Name], msg) +} + +type machineSetDiff struct { + Type string + OriginalMS *clusterv1.MachineSet + OriginalReplicas int32 + DesiredReplicas int32 + Summary string + OtherChanges string + Reason string +} + +func (p *rolloutPlanner) getMachineSetDiff(ms *clusterv1.MachineSet) machineSetDiff { + diff := machineSetDiff{} + + // Get the originalMS, if any. + originalMS, hasOriginalMS := p.originalMSs[ms.Name] + if hasOriginalMS { + diff.OriginalMS = originalMS + } + + // Determine if this is the current MS or an old one. + // Note: using "current" for logging instead of "new" to avoid confusion between new/current and new/last created. + diff.Type = "old" + if ms.Name == p.newMS.Name { + diff.Type = "current" + } + + // Retrieve original spec.replicas + // Note: Used to determine if this is a scale up or a scale down operation. + if hasOriginalMS { + diff.OriginalReplicas = ptr.Deref(originalMS.Spec.Replicas, 0) + } + + // Compute the desired spec.replicas + // Note: Used to determine if this is a scale up or a scale down operation. + diff.DesiredReplicas = ptr.Deref(ms.Spec.Replicas, 0) + + // Compute a message that summarize changes to Machine set spec replicas. + // Note. Also other replicas counters not changed by the rollout planner are included, because they are used in the computation. + if !hasOriginalMS || diff.OriginalReplicas == diff.DesiredReplicas { + diff.Summary = fmt.Sprintf("%d/%d replicas", ptr.Deref(ms.Status.Replicas, 0), diff.DesiredReplicas) + } else { + diff.Summary = fmt.Sprintf("from %d/%d to %d/%d replicas", ptr.Deref(ms.Status.Replicas, 0), diff.OriginalReplicas, ptr.Deref(ms.Status.Replicas, 0), diff.DesiredReplicas) + } + diff.Summary += fmt.Sprintf(", %d available, %d upToDate", ptr.Deref(ms.Status.AvailableReplicas, 0), ptr.Deref(ms.Status.UpToDateReplicas, 0)) + + // Compute a message with the detailed changes between current and original MS (only fields/annotation relevant for the rollout decision are included). + changes := []string{} + if hasOriginalMS && diff.OriginalReplicas != diff.DesiredReplicas { + changes = append(changes, fmt.Sprintf("replicas %d", diff.DesiredReplicas)) + } + if hasOriginalMS { + if originalMS.Annotations[clusterv1.RevisionAnnotation] != ms.Annotations[clusterv1.RevisionAnnotation] { + if value, ok := ms.Annotations[clusterv1.RevisionAnnotation]; ok { + changes = append(changes, fmt.Sprintf("%s: %s", clusterv1.RevisionAnnotation, value)) + } + } + + if originalMS.Annotations[clusterv1.MachineSetMoveMachinesToMachineSetAnnotation] != ms.Annotations[clusterv1.MachineSetMoveMachinesToMachineSetAnnotation] { + if value, ok := ms.Annotations[clusterv1.MachineSetMoveMachinesToMachineSetAnnotation]; ok { + changes = append(changes, fmt.Sprintf("%s: %s", clusterv1.MachineSetMoveMachinesToMachineSetAnnotation, value)) + } else { + changes = append(changes, fmt.Sprintf("%s removed", clusterv1.MachineSetMoveMachinesToMachineSetAnnotation)) + } + } + + if originalMS.Annotations[clusterv1.MachineSetReceiveMachinesFromMachineSetsAnnotation] != ms.Annotations[clusterv1.MachineSetReceiveMachinesFromMachineSetsAnnotation] { + if value, ok := ms.Annotations[clusterv1.MachineSetReceiveMachinesFromMachineSetsAnnotation]; ok { + changes = append(changes, fmt.Sprintf("%s: %s", clusterv1.MachineSetReceiveMachinesFromMachineSetsAnnotation, value)) + } else { + changes = append(changes, fmt.Sprintf("%s removed", clusterv1.MachineSetReceiveMachinesFromMachineSetsAnnotation)) + } + } + + if originalMS.Annotations[clusterv1.AcknowledgedMoveAnnotation] != ms.Annotations[clusterv1.AcknowledgedMoveAnnotation] { + if value, ok := ms.Annotations[clusterv1.AcknowledgedMoveAnnotation]; ok { + changes = append(changes, fmt.Sprintf("%s: %s", clusterv1.AcknowledgedMoveAnnotation, value)) + } else { + changes = append(changes, fmt.Sprintf("%s removed", clusterv1.AcknowledgedMoveAnnotation)) + } + } + + if originalMS.Annotations[clusterv1.DisableMachineCreateAnnotation] != ms.Annotations[clusterv1.DisableMachineCreateAnnotation] { + if value, ok := ms.Annotations[clusterv1.DisableMachineCreateAnnotation]; ok { + changes = append(changes, fmt.Sprintf("%s: %s", clusterv1.DisableMachineCreateAnnotation, value)) + } else { + changes = append(changes, fmt.Sprintf("%s removed", clusterv1.DisableMachineCreateAnnotation)) + } + } + + diff.OtherChanges = strings.Join(changes, ",") + } + + // Collect notes explaining the reason why something changed + diff.Reason = strings.Join(p.notes[ms.Name], ", ") + + return diff +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rollout_rollingupdate.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rollout_rollingupdate.go new file mode 100644 index 0000000000..78900a8497 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_rollout_rollingupdate.go @@ -0,0 +1,630 @@ +/* +Copyright 2018 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 machinedeployment + +import ( + "context" + "fmt" + "sort" + "strings" + + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/klog/v2" + "k8s.io/utils/ptr" + ctrl "sigs.k8s.io/controller-runtime" + + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + "sigs.k8s.io/cluster-api/feature" + "sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil" + "sigs.k8s.io/cluster-api/internal/util/inplace" + "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/collections" +) + +// rolloutRollingUpdate reconcile machine sets controlled by a MachineDeployment that is using the RolloutUpdate strategy. +func (r *Reconciler) rolloutRollingUpdate(ctx context.Context, md *clusterv1.MachineDeployment, msList []*clusterv1.MachineSet, machines collections.Machines, templateExists bool) error { + planner := newRolloutPlanner(r.Client, r.RuntimeClient, r.canUpdateMachineSetCache) + if err := planner.init(ctx, md, msList, machines.UnsortedList(), true, templateExists); err != nil { + return err + } + + if err := planner.planRollingUpdate(ctx); err != nil { + return err + } + + if err := r.createOrUpdateMachineSetsAndSyncMachineDeploymentRevision(ctx, planner); err != nil { + return err + } + + newMS := planner.newMS + oldMSs := planner.oldMSs + allMSs := append(oldMSs, newMS) + + if err := r.syncDeploymentStatus(allMSs, newMS, md); err != nil { + return err + } + + if mdutil.DeploymentComplete(md, &md.Status) { + if err := r.cleanupDeployment(ctx, oldMSs, md); err != nil { + return err + } + } + + return nil +} + +// planRollingUpdate determine how to proceed with the rollout when using the RollingUpdate strategy if the system is not yet at the desired state. +func (p *rolloutPlanner) planRollingUpdate(ctx context.Context) error { + // Adjust the replica count for the newMS after a move operation has been completed. + p.reconcileReplicasPendingAcknowledgeMove(ctx) + + // Scale up, if we can. + if err := p.reconcileNewMachineSet(ctx); err != nil { + return err + } + + // Scale down, if we can. + if err := p.reconcileOldMachineSetsRollingUpdate(ctx); err != nil { + return err + } + + // Ensures CAPI rolls out changes by performing in-place updates whenever possible. + if err := p.reconcileInPlaceUpdateIntent(ctx); err != nil { + return err + } + + // This func tries to detect and address the case when a rollout is not making progress because both scaling down and scaling up are blocked. + // Note: This func must be called after computing scale up/down intent for all the MachineSets. + // Note: This func only addresses deadlocks due to unavailable replicas not getting deleted on oldMSs, which can happen + // because reconcileOldMachineSetsRollingUpdate called above always assumes the worst case when deleting replicas e.g. + // - MD with spec.replicas 3, MaxSurge 1, MaxUnavailable 0 + // - OldMS with 3 replicas, 2 available replica (and thus 1 unavailable replica) + // - NewMS with 1 replica, 1 available replica + // - In theory it is possible to scale down oldMS from 3->2 replicas by deleting the unavailable replica. + // - However, reconcileOldMachineSetsRollingUpdate cannot assume that the MachineSet controller is going to delete + // the unavailable replica when scaling down from 3->2, because it might happen that one of the available replicas + // is deleted instead. + // - As a consequence reconcileOldMachineSetsRollingUpdate, which assumes the worst case when deleting replicas, did not scaled down oldMS. + // This situation, rollout not proceeding due to unavailable replicas, is considered a deadlock to be addressed by reconcileDeadlockBreaker. + // Note: Unblocking deadlocks when unavailable replicas exist only on oldMSs, is required also because replicas on oldMSs are not remediated by MHC. + p.reconcileDeadlockBreaker(ctx) + return nil +} + +// reconcileReplicasPendingAcknowledgeMove adjust the replica count for the newMS after a move operation has been completed. +// Note: This operation must be performed before computing scale up/down intent for all the MachineSets (so this operation can take into account also moved machines in the current reconcile). +func (p *rolloutPlanner) reconcileReplicasPendingAcknowledgeMove(ctx context.Context) { + log := ctrl.LoggerFrom(ctx) + + if !feature.Gates.Enabled(feature.InPlaceUpdates) { + return + } + + // Acknowledge replicas after a move operation. + // NOTE: PendingAcknowledgeMoveAnnotation from machine (managed by the MS controller) and AcknowledgedMoveAnnotation on the newMS (managed by the rollout planner) + // are used in combination to ensure moved replicas are counted only once by the rollout planner. + oldAcknowledgeMoveReplicas := sets.Set[string]{} + if originalMS, ok := p.originalMSs[p.newMS.Name]; ok { + if machineNames, ok := originalMS.Annotations[clusterv1.AcknowledgedMoveAnnotation]; ok && machineNames != "" { + oldAcknowledgeMoveReplicas.Insert(strings.Split(machineNames, ",")...) + } + } + newAcknowledgeMoveReplicas := sets.Set[string]{} + for _, m := range p.machines { + if !util.IsControlledBy(m, p.newMS, clusterv1.GroupVersion.WithKind("MachineSet").GroupKind()) { + continue + } + if _, ok := m.Annotations[clusterv1.PendingAcknowledgeMoveAnnotation]; !ok { + continue + } + if !oldAcknowledgeMoveReplicas.Has(m.Name) { + p.acknowledgedMachineNames = append(p.acknowledgedMachineNames, m.Name) + } + newAcknowledgeMoveReplicas.Insert(m.Name) + } + + totNewAcknowledgeMoveReplicasToScaleUp := int32(len(p.acknowledgedMachineNames)) + if totNewAcknowledgeMoveReplicasToScaleUp > 0 { + // Note: After this change the replica count will include all the newly acknowledged replicas. + // Please note that, within the same reconcile, the rollout planner might revisit replicas for the newMS + // e.g. to account for the Machine deployment being scaled up or down. + replicaCount := ptr.Deref(p.newMS.Spec.Replicas, 0) + totNewAcknowledgeMoveReplicasToScaleUp + scaleUpCount := totNewAcknowledgeMoveReplicasToScaleUp + p.newMS.Spec.Replicas = ptr.To(replicaCount) + p.addNotef(p.newMS, "acknowledge Machines %s moved from an old MachineSet", sortAndJoin(newAcknowledgeMoveReplicas.UnsortedList())) + log.V(5).Info(fmt.Sprintf("Acknowledge replicas %s moved from an old MachineSet. Scale up MachineSet %s to %d (+%d)", sortAndJoin(newAcknowledgeMoveReplicas.UnsortedList()), p.newMS.Name, replicaCount, scaleUpCount), "MachineSet", klog.KObj(p.newMS)) + } + + // Track the list or replicas for which acknowledgeMove is not yet completed; + // The MachineSetController will use this info to cleanup the PendingAcknowledgeMoveAnnotation on machines. + // NOTE: cleanup of the AcknowledgedMoveAnnotation will happen automatically as soon as the rollout planner stops + // to set it, because this annotation is not part of the output of computeDesiredMS + // (same applies to oldMS, so annotation will always be removed from oldMS). + if p.newMS.Annotations == nil { + p.newMS.Annotations = map[string]string{} + } + if newAcknowledgeMoveReplicas.Len() > 0 { + p.newMS.Annotations[clusterv1.AcknowledgedMoveAnnotation] = sortAndJoin(newAcknowledgeMoveReplicas.UnsortedList()) + } +} + +// reconcileNewMachineSet reconciles the replica number for the new MS. +// Note: In case of scale down this function does not consider the possible impact on availability. +// This is considered acceptable because historically it never led to any problem, but we might revisit this in the future +// because some limitations of this approach are becoming more evident, e.g. +// +// when users scale down the MD, the operation might temporarily breach min availability (maxUnavailable) +// +// There are code paths specifically added to prevent this issue becoming more relevant when doing in-place updates; +// e.g. the MS controller will give highest delete priority to Machines still updating in-place, +// which are also unavailable Machines. +// +// Notably, there is also no agreement yet on a different way forward because e.g. limiting scale down of the +// new MS could lead e.g. to completing in-place update of Machines that will be otherwise deleted. +func (p *rolloutPlanner) reconcileNewMachineSet(ctx context.Context) error { + log := ctrl.LoggerFrom(ctx) + allMSs := append(p.oldMSs, p.newMS) + + if *(p.newMS.Spec.Replicas) == *(p.md.Spec.Replicas) { + // Scaling not required. + return nil + } + + if *(p.newMS.Spec.Replicas) > *(p.md.Spec.Replicas) { + // Scale down. + p.addNotef(p.newMS, "scale down to align MachineSet spec.replicas to MachineDeployment spec.replicas") + log.V(5).Info(fmt.Sprintf("Setting scale down intent for MachineSet %s to %d replicas", p.newMS.Name, *(p.md.Spec.Replicas)), "MachineSet", klog.KObj(p.newMS)) + p.scaleIntents[p.newMS.Name] = *(p.md.Spec.Replicas) + return nil + } + + newReplicasCount, note, err := mdutil.NewMSNewReplicas(p.md, allMSs, *p.newMS.Spec.Replicas) + if err != nil { + return err + } + + if newReplicasCount < *(p.newMS.Spec.Replicas) { + scaleDownCount := *(p.newMS.Spec.Replicas) - newReplicasCount + p.addNotef(p.newMS, "%s", note) + log.V(5).Info(fmt.Sprintf("Setting scale down intent for MachineSet %s to %d replicas (-%d)", p.newMS.Name, newReplicasCount, scaleDownCount), "MachineSet", klog.KObj(p.newMS)) + p.scaleIntents[p.newMS.Name] = newReplicasCount + } + if newReplicasCount > *(p.newMS.Spec.Replicas) { + scaleUpCount := newReplicasCount - *(p.newMS.Spec.Replicas) + p.addNotef(p.newMS, "%s", note) + log.V(5).Info(fmt.Sprintf("Setting scale up intent for MachineSet %s to %d replicas (+%d)", p.newMS.Name, newReplicasCount, scaleUpCount), "MachineSet", klog.KObj(p.newMS)) + p.scaleIntents[p.newMS.Name] = newReplicasCount + } + return nil +} + +func (p *rolloutPlanner) reconcileOldMachineSetsRollingUpdate(ctx context.Context) error { + allMSs := append(p.oldMSs, p.newMS) + + // no op if there are no replicas on old machinesets + if mdutil.GetReplicaCountForMachineSets(p.oldMSs) == 0 { + return nil + } + + maxUnavailable := mdutil.MaxUnavailable(*p.md) + minAvailable := max(ptr.Deref(p.md.Spec.Replicas, 0)-maxUnavailable, 0) + + totalSpecReplicas := mdutil.GetReplicaCountForMachineSets(allMSs) + + // Find the total number of available replicas. + totalAvailableReplicas := ptr.Deref(mdutil.GetAvailableReplicaCountForMachineSets(allMSs), 0) + + // Find the number of pending scale down from previous reconcile/from current reconcile. + // This is required because whenever the system is reducing the number of replicas, this operation could further impact availability e.g. + // - in case of regular rollouts, there is no certainty about which replica is going to be deleted (and if the replica being deleted is currently available or not): + // - e.g. MS controller is going to first delete replicas with deletion annotation; also MS controller has a slightly different notion of unavailable as of now. + // - in case of in-place rollout, in-place updates are always assumed as impacting availability (they can always fail). + totalPendingScaleDown := int32(0) + for _, ms := range allMSs { + scaleIntent := ptr.Deref(ms.Spec.Replicas, 0) + if v, ok := p.scaleIntents[ms.Name]; ok { + scaleIntent = min(scaleIntent, v) + } + + // NOTE: Count only pending scale down from the current status.replicas (so scale down of existing replicas). + if scaleIntent < ptr.Deref(ms.Status.Replicas, 0) { + totalPendingScaleDown += max(ptr.Deref(ms.Status.Replicas, 0)-scaleIntent, 0) + } + } + + // On top of considering maxUnavailable, before scaling down it is also important to consider how unavailability is distributed + // across MachineSets, because if there are unavailable machines on the new MachineSet it is necessary to slow down or stop rollout + // to prevent to transition all the Machines to a broken state. + // In order to do so, compute the number of unavailable machines on the new MachineSet and use it to reduce totalScaleDownCount. + newMSUnavailableMachineCount := max(ptr.Deref(p.newMS.Spec.Replicas, 0)-ptr.Deref(p.newMS.Status.AvailableReplicas, 0), 0) + + // Compute the total number of replicas that can be scaled down. + // Exit immediately if there is no room for scaling down. + // NOTE: this is a quick preliminary check to verify if there is room for scaling down any of the oldMSs; further down the code + // will make additional checks to ensure scale down actually happens without breaching MaxUnavailable, and + // if necessary, it will reduce the extent of the scale down accordingly. + totalScaleDownCount := max(totalSpecReplicas-totalPendingScaleDown-minAvailable-newMSUnavailableMachineCount, 0) + if totalScaleDownCount <= 0 { + return nil + } + + // Sort oldMSs so the system will start deleting from the oldest MS first. + sort.Sort(mdutil.MachineSetsByCreationTimestamp(p.oldMSs)) + + // Scale down only unavailable replicas / up to residual totalScaleDownCount. + // NOTE: The system must scale down unavailable replicas first in order to increase chances for the rollout to progress. + // However, rollout planner must also take into account the fact that the MS controller might have a different opinion on + // which replica to delete when a scale down happens. + // + // As a consequence, the rollout planner cannot assume a scale down operation deletes an unavailable replica. e.g. + // - MD with spec.replicas 3, MaxSurge 1, MaxUnavailable 0 + // - OldMS with 3 replicas, 2 available replica (and thus 1 unavailable replica) + // - NewMS with 1 replica, 1 available replica + // - In theory it is possible to scale down oldMS from 3->2 replicas by deleting the unavailable replica. + // - However, rollout planner cannot assume that the MachineSet controller is going to delete + // the unavailable replica when scaling down from 3->2, because it might happen that one of the available replicas + // is deleted instead. + // + // In the example above, the scaleDownOldMSs should not scale down OldMS from 3->2 replicas. + // In other use cases, e.g. when scaling down an oldMS from 5->3 replicas, the scaleDownOldMSs should limit scale down extent + // only partially if this doesn't breach MaxUnavailable (e.g. scale down 5->4 instead of 5->3). + totalScaleDownCount, totalAvailableReplicas = p.scaleDownOldMSs(ctx, totalScaleDownCount, totalAvailableReplicas, minAvailable, true) + + // Then scale down old MS down to zero replicas / down to residual totalScaleDownCount. + // NOTE: Also in this case, we should continuously assess if reducing the number of replicase could further impact availability, + // and if necessary, limit scale down extent to ensure the operation respects MaxUnavailable limits. + _, _ = p.scaleDownOldMSs(ctx, totalScaleDownCount, totalAvailableReplicas, minAvailable, false) + + return nil +} + +func (p *rolloutPlanner) scaleDownOldMSs(ctx context.Context, totalScaleDownCount, totalAvailableReplicas, minAvailable int32, scaleDownOnlyUnavailableReplicas bool) (int32, int32) { + log := ctrl.LoggerFrom(ctx) + + for _, oldMS := range p.oldMSs { + // No op if there is no scaling down left. + if totalScaleDownCount <= 0 { + break + } + + replicas := ptr.Deref(oldMS.Spec.Replicas, 0) + if v, ok := p.scaleIntents[oldMS.Name]; ok { + replicas = v + } + + // No op if this MS has been already scaled down to zero. + if replicas <= 0 { + continue + } + + oldTotalAvailableReplicas := totalAvailableReplicas + + // Compute the scale down extent by considering either all replicas or, if scaleDownOnlyUnavailableReplicas is set, unavailable replicas only. + // In both cases, scale down is limited to totalScaleDownCount. + // Exit if there is no room for scaling down the MS. + // NOTE: this is a preliminary check to verify if there is room for scaling down this specific MS; further down the code + // will make additional checks to ensure scale down actually happens without breaching MaxUnavailable, and + // if necessary, it will reduce the extent of the scale down accordingly. + maxScaleDown := replicas + if scaleDownOnlyUnavailableReplicas { + maxScaleDown = max(replicas-ptr.Deref(oldMS.Status.AvailableReplicas, 0), 0) + } + scaleDown := min(maxScaleDown, totalScaleDownCount) + if scaleDown == 0 { + continue + } + + // Before scaling down validate if the operation will lead to a breach of minAvailability. + // In order to do so, consider how many existing replicas will be actually deleted, and consider + // this operation as impacting availability because there are no guarantees that the MS controller is going to + // delete unavailable replicas first; if the projected state breaches minAvailability, reduce the scale down extend accordingly. + + // If there are no available replicas on this MS, scale down won't impact totalAvailableReplicas at all. + if ptr.Deref(oldMS.Status.AvailableReplicas, 0) > 0 { + // If instead there are AvailableReplicas on this MS: + // compute the new spec.replicas assuming we are going to use the entire scale down extent. + newSpecReplicas := max(replicas-scaleDown, 0) + + // compute how many existing replicas the operation is going to delete: + // e.g. if MS is scaling down spec.replicas from 5 to 3, but status.replicas is 4, it is scaling down 1 existing replica. + // e.g. if MS is scaling down spec.replicas from 5 to 3, but status.replicas is 6, it is scaling down 3 existing replicas. + existingReplicasToBeDeleted := max(ptr.Deref(oldMS.Status.Replicas, 0)-newSpecReplicas, 0) + + // If we are deleting at least one existing replicas + if existingReplicasToBeDeleted > 0 { + // Check if we are scaling down more existing replica than what is allowed by MaxUnavailability. + if totalAvailableReplicas-minAvailable < existingReplicasToBeDeleted { + // If we are scaling down more existing replica than what is allowed by MaxUnavailability, then + // rollout planner must revisit the scale down extent. + + // Determine how many replicas can be deleted overall without further impacting availability. + maxExistingReplicasThatCanBeDeleted := max(totalAvailableReplicas-minAvailable, 0) + + // Compute the revisited new spec.replicas: + // e.g. MS spec.replicas 20, scale down 8, newSpecReplicas 12 (20-8), status.replicas 15 -> existingReplicasToBeDeleted 3 (15-12) + // assuming that maxExistingReplicasThatCanBeDeleted is 2, newSpecReplicasRevisited should be 15-2 = 13 + // e.g. MS spec.replicas 16, scale down 3, newSpecReplicas 13 (16-3), status.replicas 21 -> existingReplicasToBeDeleted 8 (21-13) + // assuming that maxExistingReplicasThatCanBeDeleted is 7, newSpecReplicasRevisited should be 21-7 = 14 + // NOTE: there is a safeguard preventing to go above the initial replicas number (this is scale down oldMS). + newSpecReplicasRevisited := min(ptr.Deref(oldMS.Status.Replicas, 0)-maxExistingReplicasThatCanBeDeleted, replicas) + + // Re-compute the scale down extent by using newSpecReplicasRevisited. + scaleDown = max(replicas-newSpecReplicasRevisited, 0) + + // Re-compute how many existing replicas the operation is going to delete by using newSpecReplicasRevisited. + existingReplicasToBeDeleted = max(ptr.Deref(oldMS.Status.Replicas, 0)-newSpecReplicasRevisited, 0) + } + + // keep track that we are deleting existing replicas by assuming that this operation + // will reduce totalAvailableReplicas (worst scenario, deletion of available machines happen first). + totalAvailableReplicas -= min(ptr.Deref(oldMS.Status.AvailableReplicas, 0), existingReplicasToBeDeleted) + } + } + + if scaleDown > 0 { + newScaleIntent := max(replicas-scaleDown, 0) + // Check if totalAvailableReplicas has been changed above and use it as a signal to determine if scale down + // was to align to existing Machines (replicas count > number of machines) or if scale down was performed + // removing unavailable replicas when no available replicas exist on the MachineSet. + // Note. In both cases overall availability is not impacted. + if oldTotalAvailableReplicas == totalAvailableReplicas { + p.addNotef(oldMS, "scale down to align to existing Machines or scale down by removing unavailable replicas (and no available replicas exist on the MachineSet)") + } else { + p.addNotef(oldMS, "%d available replicas > %d minimum available replicas", oldTotalAvailableReplicas, minAvailable) + } + log.V(5).Info(fmt.Sprintf("Setting scale down intent for MachineSet %s to %d replicas (-%d)", oldMS.Name, newScaleIntent, scaleDown), "MachineSet", klog.KObj(oldMS)) + p.scaleIntents[oldMS.Name] = newScaleIntent + totalScaleDownCount = max(totalScaleDownCount-scaleDown, 0) + } + } + + return totalScaleDownCount, totalAvailableReplicas +} + +// reconcileInPlaceUpdateIntent ensures CAPI rolls out changes by performing in-place updates whenever possible. +// +// When calling this func, new and old MS already have their scale intent, which was computed under the assumption that +// rollout is going to happen by delete/re-create, and thus it will impact availability. +// +// Also in-place updates are assumed to impact availability, even if the in-place update technically is not impacting workloads, +// the system must account for scenarios when the operation fails, leading to remediation of the machine/unavailability. +// +// As a consequence: +// - this function can rely on scale intent previously computed, and just influence how rollout is performed. +// - unless the user accounts for this unavailability by setting MaxUnavailable >= 1, +// rollout with in-place will create one additional machine to ensure MaxUnavailable == 0 is respected. +// +// NOTE: if an in-place update is possible and maxSurge is >= 1, creation of additional machines due to maxSurge is capped to 1 or entirely dropped. +// Instead, creation of new machines due to scale up goes through as usual. +func (p *rolloutPlanner) reconcileInPlaceUpdateIntent(ctx context.Context) error { + log := ctrl.LoggerFrom(ctx) + + if !feature.Gates.Enabled(feature.InPlaceUpdates) { + return nil + } + + // If new MS already has all desired replicas, it does not make sense to perform more in-place updates. + if ptr.Deref(p.newMS.Spec.Replicas, 0) >= ptr.Deref(p.md.Spec.Replicas, 0) { + return nil + } + + // Find if there are oldMSs for which it possible to perform an in-place update. + inPlaceUpdateCandidates := sets.Set[string]{} + for _, oldMS := range p.oldMSs { + // If the oldMS doesn't have replicas anymore, nothing left to do. + if ptr.Deref(oldMS.Status.Replicas, 0) <= 0 { + continue + } + + // If the oldMS is not eligible for in-place updates, move to the next MachineSet. + if result, ok := p.upToDateResults[oldMS.Name]; !ok || !result.EligibleForInPlaceUpdate { + continue + } + + // Check if the MachineSet can update in place; if not, move to the next MachineSet. + canUpdateInPlace, err := p.canUpdateMachineSetInPlace(ctx, oldMS, p.newMS) + if err != nil { + return errors.Wrapf(err, "failed to determine if MachineSet %s can be updated in-place", oldMS.Name) + } + log.V(5).Info(fmt.Sprintf("CanUpdate in-place decision for MachineSet %s: %t", oldMS.Name, canUpdateInPlace), "MachineSet", klog.KObj(oldMS)) + + if !canUpdateInPlace { + continue + } + + // Set the annotation informing the oldMS that it must move machines to the newMS instead of deleting them. + // Note: After a machine is moved from oldMS to newMS, the newMS will take care of the in-place update process. + // Note: Cleanup of the MachineSetMoveMachinesToMachineSetAnnotation will happen automatically as soon as the rollout planner stops + // to set it, because this annotation is not part of the output of computeDesiredMS + // (same applies to newMS, so annotation will always be removed from newMS). + if oldMS.Annotations == nil { + oldMS.Annotations = map[string]string{} + } + p.addNotef(oldMS, "should scale down by moving Machines to MachineSet %s", p.newMS.Name) + oldMS.Annotations[clusterv1.MachineSetMoveMachinesToMachineSetAnnotation] = p.newMS.Name + inPlaceUpdateCandidates.Insert(oldMS.Name) + } + + // If there are no inPlaceUpdateCandidates, nothing left to do. + if inPlaceUpdateCandidates.Len() <= 0 { + return nil + } + + // Set the annotation informing the newMS that it will receive replicas moved from oldMS selected as in-place candidates. + // Note: there is a two-ways check before the move operation: + // "oldMS must have: move to newMS" and "newMS must have: accept replicas from oldMS" + // Note: Cleanup of the MachineSetReceiveMachinesFromMachineSetsAnnotation will happen automatically as soon as the rollout planner stops + // to set it, because this annotation is not part of the output of computeDesiredMS + // (same applies to oldMS, so annotation will always be removed from oldMS). + if p.newMS.Annotations == nil { + p.newMS.Annotations = map[string]string{} + } + p.newMS.Annotations[clusterv1.MachineSetReceiveMachinesFromMachineSetsAnnotation] = sortAndJoin(inPlaceUpdateCandidates.UnsortedList()) + + // At this point, rollout planner know that scale down of at least one machine set is going to happen using move, and this in-place updates. + // + // Everything below this point is about checking if rollout planner is using maxSurge, + // and if possible, drop the usage of maxSurge / minimize unnecessary Machine creations because rollout planner + // must give priority to in-place when possible. + + // If the newMS is not scaling up, nothing left to do. + if scaleIntent, ok := p.scaleIntents[p.newMS.Name]; !ok || scaleIntent <= ptr.Deref(p.newMS.Spec.Replicas, 0) { + return nil + } + + // Check if the current scale up intent is using maxSurge. + scaleUpCount := p.scaleIntents[p.newMS.Name] - ptr.Deref(p.newMS.Spec.Replicas, 0) + scaleUpCountWithoutMaxSurge := max(scaleUpCount-mdutil.MaxSurge(*p.md), 0) + maxSurgeUsed := scaleUpCount - scaleUpCountWithoutMaxSurge + if maxSurgeUsed <= 0 { + // current scale up intent for the newMS is not using maxSurge, no need to revisit it. + return nil + } + + // Otherwise, the current scale up intent for the newMS is using maxSurge. + // In this case, rollout planner must prioritize in-place updates over creation of new Machines. + // So it is required to revisit the scale up intent for the newMS and defer creation of new + // Machines due to maxSurge if possible. + newScaleUpCount := scaleUpCountWithoutMaxSurge + + // If the revisited scale up count for the newMS is 0 and the newMS is not already scaling up from a previous reconcile (no scale up at all), + // check if the rollout planner is required to use one slot from MaxSurge e.g. to start a rollout. + // Note: Rollout planner will use one slot from MaxSurge - one and not more - only if: + // - The rollout is not progressing in other ways (on top of newMS not scaling from a previous reconcile, there are also no oldMS scaling down) + // - There are no in-place updates in progress (the rollout planner must wait for in-place updates to complete or fail/go + // through remediation before creating additional machines) + if newScaleUpCount == 0 && !p.scalingOrInPlaceUpdateInProgress(ctx) { + newScaleUpCount = 1 + p.addNotef(p.newMS, "surge 1 allowed to create availability for in-place updates") + } else { + p.addNotef(p.newMS, "surge %d dropped to prioritize in-place updates", maxSurgeUsed) + } + + newScaleIntent := ptr.Deref(p.newMS.Spec.Replicas, 0) + newScaleUpCount + log.V(5).Info(fmt.Sprintf("Revisited scale up intent for MachineSet %s to %d replicas (+%d) to prevent creation of new machines while there are still in-place updates to be performed", p.newMS.Name, newScaleIntent, newScaleUpCount), "MachineSet", klog.KObj(p.newMS)) + if newScaleUpCount == 0 { + delete(p.scaleIntents, p.newMS.Name) + } else { + p.scaleIntents[p.newMS.Name] = newScaleIntent + } + + return nil +} + +func (p *rolloutPlanner) scalingOrInPlaceUpdateInProgress(_ context.Context) bool { + // Check if the new MS or old MS are scaling. + if ptr.Deref(p.newMS.Spec.Replicas, 0) != ptr.Deref(p.newMS.Status.Replicas, 0) { + return true + } + for _, oldMS := range p.oldMSs { + if ptr.Deref(oldMS.Spec.Replicas, 0) < ptr.Deref(oldMS.Status.Replicas, 0) { + return true + } + if scaleIntent, ok := p.scaleIntents[oldMS.Name]; ok && scaleIntent < ptr.Deref(oldMS.Spec.Replicas, 0) { + return true + } + } + + // Check that there are no updates in progress. + // We check both that the newMS MachineSet will report that the update is completed via .status.upToDateReplicas + // and the Machine controller through the annotations so this code does not depend on a specific execution order + // of the MachineSet and Machine controllers. + if ptr.Deref(p.newMS.Spec.Replicas, 0) != ptr.Deref(p.newMS.Status.UpToDateReplicas, 0) { + return true + } + for _, m := range p.machines { + if inplace.IsUpdateInProgress(m) { + p.updatingMachineNames = append(p.updatingMachineNames, m.Name) + } + } + if len(p.updatingMachineNames) > 0 { + return true + } + + // We are also checking AvailableReplicas because we want to make sure that we wait until the + // Machine goes back to Available after the in-place update is completed. + // If we would not wait for this, the rolloutPlaner would use maxSurge to create an additional Machine. + // Note: This also means that if any Machine of the new MachineSet becomes unavailable we are blocking + // further progress of the in-place update. + if ptr.Deref(p.newMS.Spec.Replicas, 0) != ptr.Deref(p.newMS.Status.AvailableReplicas, 0) { + return true + } + return false +} + +// This funcs tries to detect and address the case when a rollout is not making progress because both scaling down and scaling up are blocked. +// Note: This func must be called after computing scale up/down intent for all the MachineSets. +// Note: This func only address deadlock due to unavailable machines not getting deleted on oldMSs, e.g. due to a wrong configuration. +// Note: Unblocking deadlocks when unavailable replicas exist only on oldMSs, is required also because replicas on oldMSs are not remediated by MHC. +func (p *rolloutPlanner) reconcileDeadlockBreaker(ctx context.Context) { + log := ctrl.LoggerFrom(ctx) + allMSs := append(p.oldMSs, p.newMS) + + // if there are no replicas on the old MS, rollout is completed, no deadlock (actually no rollout in progress). + if ptr.Deref(mdutil.GetActualReplicaCountForMachineSets(p.oldMSs), 0) == 0 { + return + } + + // if all the replicas on OldMS are available, no deadlock (regular scale up newMS and scale down oldMS should take over from here). + if ptr.Deref(mdutil.GetActualReplicaCountForMachineSets(p.oldMSs), 0) == ptr.Deref(mdutil.GetAvailableReplicaCountForMachineSets(p.oldMSs), 0) { + return + } + + // If there are scale operation in progress, no deadlock. + // Note: we are considering both scale operation from previous and current reconcile. + for _, ms := range allMSs { + if ptr.Deref(ms.Spec.Replicas, 0) != ptr.Deref(ms.Status.Replicas, 0) { + return + } + if _, ok := p.scaleIntents[ms.Name]; ok { + return + } + } + + // if there are unavailable replicas on the newMS, wait for them to become available first. + // Note: A rollout cannot be unblocked if new machines do not become available. + // Note: If the replicas on the newMS are not becoming available either: + // - automatic remediation can help in addressing temporary failures. + // - user intervention is required to fix more permanent issues e.g. to fix a wrong configuration. + if ptr.Deref(p.newMS.Status.AvailableReplicas, 0) != ptr.Deref(p.newMS.Status.Replicas, 0) { + return + } + + // At this point we can assume there is a deadlock that can only be remediated by breaching maxUnavailability constraint + // and scaling down an oldMS with unavailable machines by one. + // + // Note: In most cases this is only a formal violation of maxUnavailability, because there is a good chance + // that the machine that will be deleted is one of the unavailable machines. + // Note: This for loop relies on the same ordering of oldMSs that has been applied by reconcileOldMachineSetsRollingUpdate. + for _, oldMS := range p.oldMSs { + if ptr.Deref(oldMS.Status.AvailableReplicas, 0) == ptr.Deref(oldMS.Status.Replicas, 0) || ptr.Deref(oldMS.Spec.Replicas, 0) == 0 { + continue + } + + newScaleIntent := max(ptr.Deref(oldMS.Spec.Replicas, 0)-1, 0) + p.addNotef(p.newMS, "scaling down by 1 to unblock rollout stuck due to unavailable Machine on oldMS") + log.Info(fmt.Sprintf("Setting scale down intent for MachineSet %s to %d replicas (-%d) to unblock rollout stuck due to unavailable Machine on oldMS", oldMS.Name, newScaleIntent, 1), "MachineSet", klog.KObj(oldMS)) + p.scaleIntents[oldMS.Name] = newScaleIntent + return + } +} + +func sortAndJoin(a []string) string { + sort.Strings(a) + return strings.Join(a, ",") +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_status.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_status.go index 559f1db69e..e353827ed7 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_status.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_status.go @@ -29,7 +29,6 @@ import ( "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil" @@ -40,21 +39,8 @@ import ( func (r *Reconciler) updateStatus(ctx context.Context, s *scope) (retErr error) { // Get all Machines controlled by this MachineDeployment. - var machines, machinesToBeRemediated, unhealthyMachines collections.Machines - var getMachinesSucceeded bool - if selectorMap, err := metav1.LabelSelectorAsMap(&s.machineDeployment.Spec.Selector); err == nil { - machineList := &clusterv1.MachineList{} - if err := r.Client.List(ctx, machineList, client.InNamespace(s.machineDeployment.Namespace), client.MatchingLabels(selectorMap)); err != nil { - retErr = errors.Wrap(err, "failed to list machines") - } else { - getMachinesSucceeded = true - machines = collections.FromMachineList(machineList) - machinesToBeRemediated = machines.Filter(collections.IsUnhealthyAndOwnerRemediated) - unhealthyMachines = machines.Filter(collections.IsUnhealthy) - } - } else { - retErr = errors.Wrap(err, "failed to convert label selector to a map") - } + machinesToBeRemediated := s.machines.Filter(collections.IsUnhealthyAndOwnerRemediated) + unhealthyMachines := s.machines.Filter(collections.IsUnhealthy) // Copy label selector to its status counterpart in string format. // This is necessary for CRDs including scale subresources. @@ -72,16 +58,16 @@ func (r *Reconciler) updateStatus(ctx context.Context, s *scope) (retErr error) setAvailableCondition(ctx, s.machineDeployment, s.getAndAdoptMachineSetsForDeploymentSucceeded) - setRollingOutCondition(ctx, s.machineDeployment, machines, getMachinesSucceeded) + setRollingOutCondition(ctx, s.machineDeployment, s.machines) setScalingUpCondition(ctx, s.machineDeployment, s.machineSets, s.bootstrapTemplateNotFound, s.infrastructureTemplateNotFound, s.getAndAdoptMachineSetsForDeploymentSucceeded) - setScalingDownCondition(ctx, s.machineDeployment, s.machineSets, machines, s.getAndAdoptMachineSetsForDeploymentSucceeded, getMachinesSucceeded) + setScalingDownCondition(ctx, s.machineDeployment, s.machineSets, s.machines, s.getAndAdoptMachineSetsForDeploymentSucceeded) - setMachinesReadyCondition(ctx, s.machineDeployment, machines, getMachinesSucceeded) - setMachinesUpToDateCondition(ctx, s.machineDeployment, machines, getMachinesSucceeded) + setMachinesReadyCondition(ctx, s.machineDeployment, s.machines) + setMachinesUpToDateCondition(ctx, s.machineDeployment, s.machines) - setRemediatingCondition(ctx, s.machineDeployment, machinesToBeRemediated, unhealthyMachines, getMachinesSucceeded) + setRemediatingCondition(ctx, s.machineDeployment, machinesToBeRemediated, unhealthyMachines) - setDeletingCondition(ctx, s.machineDeployment, s.machineSets, machines, s.getAndAdoptMachineSetsForDeploymentSucceeded, getMachinesSucceeded) + setDeletingCondition(ctx, s.machineDeployment, s.machineSets, s.machines, s.getAndAdoptMachineSetsForDeploymentSucceeded) return retErr } @@ -187,18 +173,7 @@ func setAvailableCondition(_ context.Context, machineDeployment *clusterv1.Machi }) } -func setRollingOutCondition(_ context.Context, machineDeployment *clusterv1.MachineDeployment, machines collections.Machines, getMachinesSucceeded bool) { - // If we got unexpected errors in listing the machines (this should never happen), surface them. - if !getMachinesSucceeded { - conditions.Set(machineDeployment, metav1.Condition{ - Type: clusterv1.MachineDeploymentRollingOutCondition, - Status: metav1.ConditionUnknown, - Reason: clusterv1.MachineDeploymentRollingOutInternalErrorReason, - Message: "Please check controller logs for errors", - }) - return - } - +func setRollingOutCondition(_ context.Context, machineDeployment *clusterv1.MachineDeployment, machines collections.Machines) { // Count machines rolling out and collect reasons why a rollout is happening. // Note: The code below collects all the reasons for which at least a machine is rolling out; under normal circumstances // all the machines are rolling out for the same reasons, however, in case of changes to @@ -311,7 +286,7 @@ func setScalingUpCondition(_ context.Context, machineDeployment *clusterv1.Machi }) } -func setScalingDownCondition(_ context.Context, machineDeployment *clusterv1.MachineDeployment, machineSets []*clusterv1.MachineSet, machines collections.Machines, getAndAdoptMachineSetsForDeploymentSucceeded, getMachinesSucceeded bool) { +func setScalingDownCondition(_ context.Context, machineDeployment *clusterv1.MachineDeployment, machineSets []*clusterv1.MachineSet, machines collections.Machines, getAndAdoptMachineSetsForDeploymentSucceeded bool) { // If we got unexpected errors in listing the machines sets (this should never happen), surface them. if !getAndAdoptMachineSetsForDeploymentSucceeded { conditions.Set(machineDeployment, metav1.Condition{ @@ -343,11 +318,9 @@ func setScalingDownCondition(_ context.Context, machineDeployment *clusterv1.Mac // Scaling down. if currentReplicas > desiredReplicas { message := fmt.Sprintf("Scaling down from %d to %d replicas", currentReplicas, desiredReplicas) - if getMachinesSucceeded { - staleMessage := aggregateStaleMachines(machines) - if staleMessage != "" { - message += fmt.Sprintf("\n* %s", staleMessage) - } + staleMessage := aggregateStaleMachines(machines) + if staleMessage != "" { + message += fmt.Sprintf("\n* %s", staleMessage) } conditions.Set(machineDeployment, metav1.Condition{ Type: clusterv1.MachineDeploymentScalingDownCondition, @@ -366,19 +339,8 @@ func setScalingDownCondition(_ context.Context, machineDeployment *clusterv1.Mac }) } -func setMachinesReadyCondition(ctx context.Context, machineDeployment *clusterv1.MachineDeployment, machines collections.Machines, getMachinesSucceeded bool) { +func setMachinesReadyCondition(ctx context.Context, machineDeployment *clusterv1.MachineDeployment, machines collections.Machines) { log := ctrl.LoggerFrom(ctx) - // If we got unexpected errors in listing the machines (this should never happen), surface them. - if !getMachinesSucceeded { - conditions.Set(machineDeployment, metav1.Condition{ - Type: clusterv1.MachineDeploymentMachinesReadyCondition, - Status: metav1.ConditionUnknown, - Reason: clusterv1.MachineDeploymentMachinesReadyInternalErrorReason, - Message: "Please check controller logs for errors", - }) - return - } - if len(machines) == 0 { conditions.Set(machineDeployment, metav1.Condition{ Type: clusterv1.MachineDeploymentMachinesReadyCondition, @@ -416,19 +378,8 @@ func setMachinesReadyCondition(ctx context.Context, machineDeployment *clusterv1 conditions.Set(machineDeployment, *readyCondition) } -func setMachinesUpToDateCondition(ctx context.Context, machineDeployment *clusterv1.MachineDeployment, machines collections.Machines, getMachinesSucceeded bool) { +func setMachinesUpToDateCondition(ctx context.Context, machineDeployment *clusterv1.MachineDeployment, machines collections.Machines) { log := ctrl.LoggerFrom(ctx) - // If we got unexpected errors in listing the machines (this should never happen), surface them. - if !getMachinesSucceeded { - conditions.Set(machineDeployment, metav1.Condition{ - Type: clusterv1.MachineDeploymentMachinesUpToDateCondition, - Status: metav1.ConditionUnknown, - Reason: clusterv1.MachineDeploymentMachinesUpToDateInternalErrorReason, - Message: "Please check controller logs for errors", - }) - return - } - // Only consider Machines that have an UpToDate condition or are older than 10s. // This is done to ensure the MachinesUpToDate condition doesn't flicker after a new Machine is created, // because it can take a bit until the UpToDate condition is set on a new Machine. @@ -473,17 +424,7 @@ func setMachinesUpToDateCondition(ctx context.Context, machineDeployment *cluste conditions.Set(machineDeployment, *upToDateCondition) } -func setRemediatingCondition(ctx context.Context, machineDeployment *clusterv1.MachineDeployment, machinesToBeRemediated, unhealthyMachines collections.Machines, getMachinesSucceeded bool) { - if !getMachinesSucceeded { - conditions.Set(machineDeployment, metav1.Condition{ - Type: clusterv1.MachineDeploymentRemediatingCondition, - Status: metav1.ConditionUnknown, - Reason: clusterv1.MachineDeploymentRemediatingInternalErrorReason, - Message: "Please check controller logs for errors", - }) - return - } - +func setRemediatingCondition(ctx context.Context, machineDeployment *clusterv1.MachineDeployment, machinesToBeRemediated, unhealthyMachines collections.Machines) { if len(machinesToBeRemediated) == 0 { message := aggregateUnhealthyMachines(unhealthyMachines) conditions.Set(machineDeployment, metav1.Condition{ @@ -522,9 +463,9 @@ func setRemediatingCondition(ctx context.Context, machineDeployment *clusterv1.M }) } -func setDeletingCondition(_ context.Context, machineDeployment *clusterv1.MachineDeployment, machineSets []*clusterv1.MachineSet, machines collections.Machines, getAndAdoptMachineSetsForDeploymentSucceeded, getMachinesSucceeded bool) { +func setDeletingCondition(_ context.Context, machineDeployment *clusterv1.MachineDeployment, machineSets []*clusterv1.MachineSet, machines collections.Machines, getAndAdoptMachineSetsForDeploymentSucceeded bool) { // If we got unexpected errors in listing the machines sets or machines (this should never happen), surface them. - if !getAndAdoptMachineSetsForDeploymentSucceeded || !getMachinesSucceeded { + if !getAndAdoptMachineSetsForDeploymentSucceeded { conditions.Set(machineDeployment, metav1.Condition{ Type: clusterv1.MachineDeploymentDeletingCondition, Status: metav1.ConditionUnknown, diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_sync.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_sync.go index e146507a39..c5a08c40fc 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_sync.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/machinedeployment_sync.go @@ -20,16 +20,11 @@ import ( "context" "fmt" "sort" - "time" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - kerrors "k8s.io/apimachinery/pkg/util/errors" apirand "k8s.io/apimachinery/pkg/util/rand" - "k8s.io/apimachinery/pkg/util/wait" "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" @@ -37,298 +32,53 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil" - "sigs.k8s.io/cluster-api/internal/util/hash" - "sigs.k8s.io/cluster-api/internal/util/ssa" + clientutil "sigs.k8s.io/cluster-api/internal/util/client" + "sigs.k8s.io/cluster-api/util/collections" v1beta1conditions "sigs.k8s.io/cluster-api/util/conditions/deprecated/v1beta1" "sigs.k8s.io/cluster-api/util/patch" ) // sync is responsible for reconciling deployments on scaling events or when they // are paused. -func (r *Reconciler) sync(ctx context.Context, md *clusterv1.MachineDeployment, msList []*clusterv1.MachineSet, templateExists bool) error { - newMS, oldMSs, err := r.getAllMachineSetsAndSyncRevision(ctx, md, msList, false, templateExists) - if err != nil { +func (r *Reconciler) sync(ctx context.Context, md *clusterv1.MachineDeployment, msList []*clusterv1.MachineSet, machines collections.Machines, templateExists bool) error { + // Use the rollout planner to take benefit of the common logic for: + // - identifying newMS and OldMS when necessary + // - computing desired state for newMS and OldMS, including managing rollout related annotations and + // in-place propagation of labels, annotations and other fields. + planner := newRolloutPlanner(r.Client, r.RuntimeClient, r.canUpdateMachineSetCache) + if err := planner.init(ctx, md, msList, machines.UnsortedList(), false, templateExists); err != nil { return err } - if err := r.scale(ctx, md, newMS, oldMSs); err != nil { - // If we get an error while trying to scale, the deployment will be requeued - // so we can abort this resync + // Applying above changes to MachineSets, so it will be possible to use legacy code for scale. + if err := r.createOrUpdateMachineSetsAndSyncMachineDeploymentRevision(ctx, planner); err != nil { return err } - // - // // TODO: Clean up the deployment when it's paused and no rollback is in flight. - // + // Call the legacy scale logic. + // Note: the legacy scale logic do not relies yet on the rollout planner, and it still lead to many + // patch calls vs grouping all the MachineSet changes in a single SSA call based on a carefully crafted desired state. + // Note: using the legacy scale logic on newMS and oldMSs computed by the rollout planner is not an issue because + // the legacy scale logic relies on info that are part of the desired state computed by rollout planner (or of + // info carried over from original MS). More specifically: + // - ms.metadata.CreationTimestamp, carried over + // - ms.metadata.Annotations, computed (only DesiredReplicasAnnotation, MaxReplicasAnnotation are relevant) + // - ms.spec.Replicas, computed + // - ms.status.Replicas, carried over + // - ms.status.AvailableReplicas, carried over + newMS := planner.newMS + oldMSs := planner.oldMSs allMSs := append(oldMSs, newMS) - return r.syncDeploymentStatus(allMSs, newMS, md) -} - -// getAllMachineSetsAndSyncRevision returns all the machine sets for the provided deployment (new and all old), with new MS's and deployment's revision updated. -// -// msList should come from getMachineSetsForDeployment(d). -// machineMap should come from getMachineMapForDeployment(d, msList). -// -// 1. Get all old MSes this deployment targets, and calculate the max revision number among them (maxOldV). -// 2. Get new MS this deployment targets (whose machine template matches deployment's), and update new MS's revision number to (maxOldV + 1), -// only if its revision number is smaller than (maxOldV + 1). If this step failed, we'll update it in the next deployment sync loop. -// 3. Copy new MS's revision number to deployment (update deployment's revision). If this step failed, we'll update it in the next deployment sync loop. -// -// Note that currently the deployment controller is using caches to avoid querying the server for reads. -// This may lead to stale reads of machine sets, thus incorrect deployment status. -func (r *Reconciler) getAllMachineSetsAndSyncRevision(ctx context.Context, md *clusterv1.MachineDeployment, msList []*clusterv1.MachineSet, createIfNotExisted, templateExists bool) (*clusterv1.MachineSet, []*clusterv1.MachineSet, error) { - reconciliationTime := metav1.Now() - allOldMSs, err := mdutil.FindOldMachineSets(md, msList, &reconciliationTime) - if err != nil { - return nil, nil, err - } - - // Get new machine set with the updated revision number - newMS, err := r.getNewMachineSet(ctx, md, msList, allOldMSs, createIfNotExisted, templateExists, &reconciliationTime) - if err != nil { - return nil, nil, err - } - - return newMS, allOldMSs, nil -} - -// Returns a MachineSet that matches the intent of the given MachineDeployment. -// If there does not exist such a MachineSet and createIfNotExisted is true, create a new MachineSet. -// If there is already such a MachineSet, update it to propagate in-place mutable fields from the MachineDeployment. -func (r *Reconciler) getNewMachineSet(ctx context.Context, md *clusterv1.MachineDeployment, msList, oldMSs []*clusterv1.MachineSet, createIfNotExists, templateExists bool, reconciliationTime *metav1.Time) (*clusterv1.MachineSet, error) { - // Try to find a MachineSet which matches the MachineDeployments intent, while ignore diffs between - // the in-place mutable fields. - // If we find a matching MachineSet we just update it to propagate any changes to the in-place mutable - // fields and thus we do not trigger an unnecessary rollout (i.e. create a new MachineSet). - // If we don't find a matching MachineSet, we need a rollout and thus create a new MachineSet. - // Note: The in-place mutable fields can be just updated inline, because they do not affect the actual machines - // themselves (i.e. the infrastructure and the software running on the Machines not the Machine object). - matchingMS, createReason, err := mdutil.FindNewMachineSet(md, msList, reconciliationTime) - if err != nil { - return nil, err - } - - // If there is a MachineSet that matches the intent of the MachineDeployment, update the MachineSet - // to propagate all in-place mutable fields from MachineDeployment to the MachineSet. - if matchingMS != nil { - updatedMS, err := r.updateMachineSet(ctx, md, matchingMS, oldMSs) - if err != nil { - return nil, err - } - - // Ensure MachineDeployment has the latest MachineSet revision in its revision annotation. - mdutil.SetDeploymentRevision(md, updatedMS.Annotations[clusterv1.RevisionAnnotation]) - return updatedMS, nil - } - - if !createIfNotExists { - return nil, nil - } - - if !templateExists { - return nil, errors.New("cannot create a new MachineSet when templates do not exist") - } - - // Create a new MachineSet and wait until the new MachineSet exists in the cache. - newMS, err := r.createMachineSetAndWait(ctx, md, oldMSs, createReason) - if err != nil { - return nil, err - } - - mdutil.SetDeploymentRevision(md, newMS.Annotations[clusterv1.RevisionAnnotation]) - - return newMS, nil -} - -// updateMachineSet updates an existing MachineSet to propagate in-place mutable fields from the MachineDeployment. -func (r *Reconciler) updateMachineSet(ctx context.Context, deployment *clusterv1.MachineDeployment, ms *clusterv1.MachineSet, oldMSs []*clusterv1.MachineSet) (*clusterv1.MachineSet, error) { - log := ctrl.LoggerFrom(ctx) - - // Compute the desired MachineSet. - updatedMS, err := r.computeDesiredMachineSet(ctx, deployment, ms, oldMSs) - if err != nil { - return nil, errors.Wrapf(err, "failed to update MachineSet %q", klog.KObj(ms)) - } - // Update the MachineSet to propagate in-place mutable fields from the MachineDeployment. - err = ssa.Patch(ctx, r.Client, machineDeploymentManagerName, updatedMS, ssa.WithCachingProxy{Cache: r.ssaCache, Original: ms}) - if err != nil { - r.recorder.Eventf(deployment, corev1.EventTypeWarning, "FailedUpdate", "Failed to update MachineSet %s: %v", klog.KObj(updatedMS), err) - return nil, errors.Wrapf(err, "failed to update MachineSet %s", klog.KObj(updatedMS)) - } - - log.V(4).Info("Updated MachineSet", "MachineSet", klog.KObj(updatedMS)) - return updatedMS, nil -} - -// createMachineSetAndWait creates a new MachineSet with the desired intent of the MachineDeployment. -// It waits for the cache to be updated with the newly created MachineSet. -func (r *Reconciler) createMachineSetAndWait(ctx context.Context, deployment *clusterv1.MachineDeployment, oldMSs []*clusterv1.MachineSet, createReason string) (*clusterv1.MachineSet, error) { - log := ctrl.LoggerFrom(ctx) - - // Compute the desired MachineSet. - newMS, err := r.computeDesiredMachineSet(ctx, deployment, nil, oldMSs) - if err != nil { - return nil, errors.Wrap(err, "failed to create new MachineSet") - } - - log = log.WithValues("MachineSet", klog.KObj(newMS)) - ctx = ctrl.LoggerInto(ctx, log) - - // Create the MachineSet. - if err := ssa.Patch(ctx, r.Client, machineDeploymentManagerName, newMS); err != nil { - r.recorder.Eventf(deployment, corev1.EventTypeWarning, "FailedCreate", "Failed to create MachineSet %s: %v", klog.KObj(newMS), err) - return nil, errors.Wrapf(err, "failed to create new MachineSet %s", klog.KObj(newMS)) - } - log.Info(fmt.Sprintf("MachineSet created (%s)", createReason)) - r.recorder.Eventf(deployment, corev1.EventTypeNormal, "SuccessfulCreate", "Created MachineSet %s", klog.KObj(newMS)) - - // Keep trying to get the MachineSet. This will force the cache to update and prevent any future reconciliation of - // the MachineDeployment to reconcile with an outdated list of MachineSets which could lead to unwanted creation of - // a duplicate MachineSet. - var pollErrors []error - if err := wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 10*time.Second, true, func(ctx context.Context) (bool, error) { - ms := &clusterv1.MachineSet{} - if err := r.Client.Get(ctx, client.ObjectKeyFromObject(newMS), ms); err != nil { - // Do not return error here. Continue to poll even if we hit an error - // so that we avoid existing because of transient errors like network flakes. - // Capture all the errors and return the aggregate error if the poll fails eventually. - pollErrors = append(pollErrors, err) - return false, nil - } - return true, nil - }); err != nil { - return nil, errors.Wrapf(kerrors.NewAggregate(pollErrors), "failed to get the MachineSet %s after creation", klog.KObj(newMS)) - } - return newMS, nil -} - -// computeDesiredMachineSet computes the desired MachineSet. -// This MachineSet will be used during reconciliation to: -// * create a MachineSet -// * update an existing MachineSet -// Because we are using Server-Side-Apply we always have to calculate the full object. -// There are small differences in how we calculate the MachineSet depending on if it -// is a create or update. Example: for a new MachineSet we have to calculate a new name, -// while for an existing MachineSet we have to use the name of the existing MachineSet. -func (r *Reconciler) computeDesiredMachineSet(ctx context.Context, deployment *clusterv1.MachineDeployment, existingMS *clusterv1.MachineSet, oldMSs []*clusterv1.MachineSet) (*clusterv1.MachineSet, error) { - var name string - var uid types.UID - var finalizers []string - var uniqueIdentifierLabelValue string - var machineTemplateSpec clusterv1.MachineSpec - var replicas int32 - var err error - - // For a new MachineSet: - // * compute a new uniqueIdentifier, a new MachineSet name, finalizers, replicas and - // machine template spec (take the one from MachineDeployment) - if existingMS == nil { - // Note: In previous Cluster API versions (< v1.4.0), the label value was the hash of the full machine - // template. With the introduction of in-place mutation the machine template of the MachineSet can change. - // Because of that it is impossible that the label's value to always be the hash of the full machine template. - // (Because the hash changes when the machine template changes). - // As a result, we use the hash of the machine template while ignoring all in-place mutable fields, i.e. the - // machine template with only fields that could trigger a rollout for the machine-template-hash, making it - // independent of the changes to any in-place mutable fields. - templateHash, err := hash.Compute(mdutil.MachineTemplateDeepCopyRolloutFields(&deployment.Spec.Template)) - if err != nil { - return nil, errors.Wrap(err, "failed to compute desired MachineSet: failed to compute machine template hash") - } - // Append a random string at the end of template hash. This is required to distinguish MachineSets that - // could be created with the same spec as a result of rolloutAfter. If not, computeDesiredMachineSet - // will end up updating the existing MachineSet instead of creating a new one. - var randomSuffix string - name, randomSuffix = computeNewMachineSetName(deployment.Name + "-") - uniqueIdentifierLabelValue = fmt.Sprintf("%d-%s", templateHash, randomSuffix) - - replicas, err = mdutil.NewMSNewReplicas(deployment, oldMSs, 0) - if err != nil { - return nil, errors.Wrap(err, "failed to compute desired MachineSet") - } - - machineTemplateSpec = *deployment.Spec.Template.Spec.DeepCopy() - } else { - // For updating an existing MachineSet: - // * get the uniqueIdentifier from labels of the existingMS - // * use name, uid, finalizers, replicas and machine template spec from existingMS. - // Note: We use the uid, to ensure that the Server-Side-Apply only updates existingMS. - // Note: We carry over those fields because we don't want to mutate them for an existingMS. - var uniqueIdentifierLabelExists bool - uniqueIdentifierLabelValue, uniqueIdentifierLabelExists = existingMS.Labels[clusterv1.MachineDeploymentUniqueLabel] - if !uniqueIdentifierLabelExists { - return nil, errors.Errorf("failed to compute desired MachineSet: failed to get unique identifier from %q annotation", - clusterv1.MachineDeploymentUniqueLabel) - } - - name = existingMS.Name - uid = existingMS.UID - - // Preserve all existing finalizers (including foregroundDeletion finalizer). - finalizers = existingMS.Finalizers - - replicas = *existingMS.Spec.Replicas - - machineTemplateSpec = *existingMS.Spec.Template.Spec.DeepCopy() - } - - // Construct the basic MachineSet. - desiredMS := &clusterv1.MachineSet{ - TypeMeta: metav1.TypeMeta{ - APIVersion: clusterv1.GroupVersion.String(), - Kind: "MachineSet", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: deployment.Namespace, - // Note: By setting the ownerRef on creation we signal to the MachineSet controller that this is not a stand-alone MachineSet. - OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(deployment, machineDeploymentKind)}, - UID: uid, - Finalizers: finalizers, - }, - Spec: clusterv1.MachineSetSpec{ - Replicas: &replicas, - ClusterName: deployment.Spec.ClusterName, - Template: clusterv1.MachineTemplateSpec{ - Spec: machineTemplateSpec, - }, - }, + // Note: Consider if to move the scale logic to the rollout planner as well, so we can improve test coverage + // like we did for RolloutUpdate and OnDelete strategy. + if err := r.scale(ctx, md, newMS, oldMSs); err != nil { + // If we get an error while trying to scale, the deployment will be requeued + // so we can abort this resync + return err } - // Set the in-place mutable fields. - // When we create a new MachineSet we will just create the MachineSet with those fields. - // When we update an existing MachineSet will we update the fields on the existing MachineSet (in-place mutate). - - // Set labels and .spec.template.labels. - desiredMS.Labels = mdutil.CloneAndAddLabel(deployment.Spec.Template.Labels, - clusterv1.MachineDeploymentUniqueLabel, uniqueIdentifierLabelValue) - // Always set the MachineDeploymentNameLabel. - // Note: If a client tries to create a MachineDeployment without a selector, the MachineDeployment webhook - // will add this label automatically. But we want this label to always be present even if the MachineDeployment - // has a selector which doesn't include it. Therefore, we have to set it here explicitly. - desiredMS.Labels[clusterv1.MachineDeploymentNameLabel] = deployment.Name - desiredMS.Spec.Template.Labels = mdutil.CloneAndAddLabel(deployment.Spec.Template.Labels, - clusterv1.MachineDeploymentUniqueLabel, uniqueIdentifierLabelValue) - - // Set selector. - desiredMS.Spec.Selector = *mdutil.CloneSelectorAndAddLabel(&deployment.Spec.Selector, clusterv1.MachineDeploymentUniqueLabel, uniqueIdentifierLabelValue) - - // Set annotations and .spec.template.annotations. - if desiredMS.Annotations, err = mdutil.ComputeMachineSetAnnotations(ctx, deployment, oldMSs, existingMS); err != nil { - return nil, errors.Wrap(err, "failed to compute desired MachineSet: failed to compute annotations") - } - desiredMS.Spec.Template.Annotations = cloneStringMap(deployment.Spec.Template.Annotations) - - // Set all other in-place mutable fields. - desiredMS.Spec.Template.Spec.MinReadySeconds = deployment.Spec.Template.Spec.MinReadySeconds - desiredMS.Spec.Deletion.Order = deployment.Spec.Deletion.Order - desiredMS.Spec.Template.Spec.ReadinessGates = deployment.Spec.Template.Spec.ReadinessGates - desiredMS.Spec.Template.Spec.Deletion.NodeDrainTimeoutSeconds = deployment.Spec.Template.Spec.Deletion.NodeDrainTimeoutSeconds - desiredMS.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds = deployment.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds - desiredMS.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = deployment.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds - desiredMS.Spec.MachineNaming = deployment.Spec.MachineNaming - - return desiredMS, nil + return r.syncDeploymentStatus(allMSs, newMS, md) } // cloneStringMap clones a string map. @@ -538,14 +288,8 @@ func (r *Reconciler) scaleMachineSet(ctx context.Context, ms *clusterv1.MachineS return errors.Errorf("spec.replicas for MachineDeployment %v is nil, this is unexpected", client.ObjectKeyFromObject(deployment)) } - annotationsNeedUpdate := mdutil.ReplicasAnnotationsNeedUpdate( - ms, - *(deployment.Spec.Replicas), - *(deployment.Spec.Replicas)+mdutil.MaxSurge(*deployment), - ) - - // No need to scale nor setting annotations, return. - if *(ms.Spec.Replicas) == newScale && !annotationsNeedUpdate { + // No need to scale, return. + if *(ms.Spec.Replicas) == newScale { return nil } @@ -558,9 +302,8 @@ func (r *Reconciler) scaleMachineSet(ctx context.Context, ms *clusterv1.MachineS // Save original replicas to log in event. originalReplicas := *(ms.Spec.Replicas) - // Mutate replicas and the related annotation. + // Mutate replicas. ms.Spec.Replicas = &newScale - mdutil.SetReplicasAnnotations(ms, *(deployment.Spec.Replicas), *(deployment.Spec.Replicas)+mdutil.MaxSurge(*deployment)) if err := patchHelper.Patch(ctx, ms); err != nil { r.recorder.Eventf(deployment, corev1.EventTypeWarning, "FailedScale", "Failed to scale MachineSet %v: %v", @@ -591,8 +334,8 @@ func (r *Reconciler) cleanupDeployment(ctx context.Context, oldMSs []*clusterv1. } sort.Sort(mdutil.MachineSetsByCreationTimestamp(cleanableMSes)) - log.V(4).Info("Looking to cleanup old machine sets for deployment") + machineSetsDeleted := []*clusterv1.MachineSet{} for i := range cleanableMSCount { ms := cleanableMSes[i] if ms.Spec.Replicas == nil { @@ -604,17 +347,18 @@ func (r *Reconciler) cleanupDeployment(ctx context.Context, oldMSs []*clusterv1. continue } - log.V(4).Info("Trying to cleanup machine set for deployment", "MachineSet", klog.KObj(ms)) if err := r.Client.Delete(ctx, ms); err != nil && !apierrors.IsNotFound(err) { // Return error instead of aggregating and continuing DELETEs on the theory // that we may be overloading the api server. r.recorder.Eventf(deployment, corev1.EventTypeWarning, "FailedDelete", "Failed to delete MachineSet %q: %v", ms.Name, err) - return err + return errors.Wrapf(err, "failed to delete MachineSet %s (cleanup of old MachineSets)", klog.KObj(ms)) } + machineSetsDeleted = append(machineSetsDeleted, ms) + // Note: We intentionally log after Delete because we want this log line to show up only after DeletionTimestamp has been set. - log.Info("Deleting MachineSet (cleanup of old MachineSet)", "MachineSet", klog.KObj(ms)) + log.Info(fmt.Sprintf("MachineSet %s deleting (cleanup of old MachineSets)", ms.Name), "MachineSet", klog.KObj(ms)) r.recorder.Eventf(deployment, corev1.EventTypeNormal, "SuccessfulDelete", "Deleted MachineSet %q", ms.Name) } - return nil + return clientutil.WaitForObjectsToBeDeletedFromTheCache(ctx, r.Client, "MachineSet deletion (cleanup of old MachineSet)", machineSetsDeleted...) } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil/util.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil/util.go index db7c4a24b9..8540b269ba 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil/util.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil/util.go @@ -102,21 +102,6 @@ func (o MachineSetsBySizeNewer) Less(i, j int) bool { return *(o[i].Spec.Replicas) > *(o[j].Spec.Replicas) } -// SetDeploymentRevision updates the revision for a deployment. -func SetDeploymentRevision(deployment *clusterv1.MachineDeployment, revision string) bool { - updated := false - - if deployment.Annotations == nil { - deployment.Annotations = make(map[string]string) - } - if deployment.Annotations[clusterv1.RevisionAnnotation] != revision { - deployment.Annotations[clusterv1.RevisionAnnotation] = revision - updated = true - } - - return updated -} - // MaxRevision finds the highest revision in the machine sets. func MaxRevision(ctx context.Context, allMSs []*clusterv1.MachineSet) int64 { log := ctrl.LoggerFrom(ctx) @@ -193,9 +178,9 @@ func getIntFromAnnotation(ms *clusterv1.MachineSet, annotationKey string, logger // Deprecated: This annotation is deprecated and is going to be removed in the next release. const revisionHistoryAnnotation = "machinedeployment.clusters.x-k8s.io/revision-history" -// ComputeMachineSetAnnotations computes the annotations that should be set on the MachineSet. -// Note: The passed in newMS is nil if the new MachineSet doesn't exist in the apiserver yet. -func ComputeMachineSetAnnotations(ctx context.Context, deployment *clusterv1.MachineDeployment, oldMSs []*clusterv1.MachineSet, newMS *clusterv1.MachineSet) (map[string]string, error) { +// MachineSetAnnotationsFromMachineDeployment return the annotations that should be set on all the MachineSets and +// that are derived from the controlling MachineDeployment. +func MachineSetAnnotationsFromMachineDeployment(_ context.Context, deployment *clusterv1.MachineDeployment) map[string]string { // Copy annotations from Deployment annotations while filtering out some annotations // that we don't want to propagate. annotations := map[string]string{} @@ -206,6 +191,17 @@ func ComputeMachineSetAnnotations(ctx context.Context, deployment *clusterv1.Mac annotations[k] = v } + annotations[clusterv1.DesiredReplicasAnnotation] = fmt.Sprintf("%d", *deployment.Spec.Replicas) + annotations[clusterv1.MaxReplicasAnnotation] = fmt.Sprintf("%d", *(deployment.Spec.Replicas)+MaxSurge(*deployment)) + return annotations +} + +// ComputeRevisionAnnotations returns revision annotations to be set on a newMS. +func ComputeRevisionAnnotations(ctx context.Context, newMS *clusterv1.MachineSet, oldMSs []*clusterv1.MachineSet) (map[string]string, string, error) { + // Copy annotations from Deployment annotations while filtering out some annotations + // that we don't want to propagate. + annotations := map[string]string{} + // The newMS's revision should be the greatest among all MSes. Usually, its revision number is newRevision (the max revision number // of all old MSes + 1). However, it's possible that some old MSes are deleted after the newMS revision being updated, and // newRevision becomes smaller than newMS's revision. We will never decrease a revision of a MachineSet. @@ -217,7 +213,7 @@ func ComputeMachineSetAnnotations(ctx context.Context, deployment *clusterv1.Mac if currentRevisionExists { currentRevisionInt, err := strconv.ParseInt(currentRevision, 10, 64) if err != nil { - return nil, errors.Wrapf(err, "failed to parse current revision on MachineSet %s", klog.KObj(newMS)) + return nil, newRevision, errors.Wrapf(err, "failed to parse current revision on MachineSet %s", klog.KObj(newMS)) } if newRevisionInt < currentRevisionInt { newRevision = currentRevision @@ -243,9 +239,19 @@ func ComputeMachineSetAnnotations(ctx context.Context, deployment *clusterv1.Mac } annotations[clusterv1.RevisionAnnotation] = newRevision - annotations[clusterv1.DesiredReplicasAnnotation] = fmt.Sprintf("%d", *deployment.Spec.Replicas) - annotations[clusterv1.MaxReplicasAnnotation] = fmt.Sprintf("%d", *(deployment.Spec.Replicas)+MaxSurge(*deployment)) - return annotations, nil + return annotations, newRevision, nil +} + +// GetRevisionAnnotations returns revision annotations to be preserved on oldMSs. +func GetRevisionAnnotations(_ context.Context, oldMS *clusterv1.MachineSet) map[string]string { + annotations := map[string]string{} + if v, ok := oldMS.Annotations[clusterv1.RevisionAnnotation]; ok { + annotations[clusterv1.RevisionAnnotation] = v + } + if v, ok := oldMS.Annotations[revisionHistoryAnnotation]; ok { + annotations[revisionHistoryAnnotation] = v + } + return annotations } // FindOneActiveOrLatest returns the only active or the latest machine set in case there is at most one active @@ -273,39 +279,6 @@ func FindOneActiveOrLatest(newMS *clusterv1.MachineSet, oldMSs []*clusterv1.Mach } } -// SetReplicasAnnotations sets the desiredReplicas and maxReplicas into the annotations. -func SetReplicasAnnotations(ms *clusterv1.MachineSet, desiredReplicas, maxReplicas int32) bool { - updated := false - if ms.Annotations == nil { - ms.Annotations = make(map[string]string) - } - desiredString := fmt.Sprintf("%d", desiredReplicas) - if hasString := ms.Annotations[clusterv1.DesiredReplicasAnnotation]; hasString != desiredString { - ms.Annotations[clusterv1.DesiredReplicasAnnotation] = desiredString - updated = true - } - if hasString := ms.Annotations[clusterv1.MaxReplicasAnnotation]; hasString != fmt.Sprintf("%d", maxReplicas) { - ms.Annotations[clusterv1.MaxReplicasAnnotation] = fmt.Sprintf("%d", maxReplicas) - updated = true - } - return updated -} - -// ReplicasAnnotationsNeedUpdate return true if the replicas annotation needs to be updated. -func ReplicasAnnotationsNeedUpdate(ms *clusterv1.MachineSet, desiredReplicas, maxReplicas int32) bool { - if ms.Annotations == nil { - return true - } - desiredString := fmt.Sprintf("%d", desiredReplicas) - if hasString := ms.Annotations[clusterv1.DesiredReplicasAnnotation]; hasString != desiredString { - return true - } - if hasString := ms.Annotations[clusterv1.MaxReplicasAnnotation]; hasString != fmt.Sprintf("%d", maxReplicas) { - return true - } - return false -} - // MaxUnavailable returns the maximum unavailable machines a rolling deployment can take. func MaxUnavailable(deployment clusterv1.MachineDeployment) int32 { if !IsRollingUpdate(&deployment) || *(deployment.Spec.Replicas) == 0 { @@ -376,16 +349,27 @@ func getMachineSetFraction(ms clusterv1.MachineSet, md clusterv1.MachineDeployme return integer.RoundToInt32(newMSsize) - *(ms.Spec.Replicas) } +// UpToDateResult is the result of calling the MachineTemplateUpToDate func for a MachineTemplateSpec. +type UpToDateResult struct { + LogMessages []string + ConditionMessages []string + EligibleForInPlaceUpdate bool +} + // MachineTemplateUpToDate returns true if the current MachineTemplateSpec is up-to-date with a corresponding desired MachineTemplateSpec. // Note: The comparison does not consider any in-place propagated fields, as well as the version from external references. -func MachineTemplateUpToDate(current, desired *clusterv1.MachineTemplateSpec) (upToDate bool, logMessages, conditionMessages []string) { +func MachineTemplateUpToDate(current, desired *clusterv1.MachineTemplateSpec) (bool, UpToDateResult) { + res := UpToDateResult{ + EligibleForInPlaceUpdate: true, + } + currentCopy := MachineTemplateDeepCopyRolloutFields(current) desiredCopy := MachineTemplateDeepCopyRolloutFields(desired) if currentCopy.Spec.Version != desiredCopy.Spec.Version { - logMessages = append(logMessages, fmt.Sprintf("spec.version %s, %s required", currentCopy.Spec.Version, desiredCopy.Spec.Version)) + res.LogMessages = append(res.LogMessages, fmt.Sprintf("spec.version %s, %s required", currentCopy.Spec.Version, desiredCopy.Spec.Version)) // Note: the code computing the message for MachineDeployment's RolloutOut condition is making assumptions on the format/content of this message. - conditionMessages = append(conditionMessages, fmt.Sprintf("Version %s, %s required", currentCopy.Spec.Version, desiredCopy.Spec.Version)) + res.ConditionMessages = append(res.ConditionMessages, fmt.Sprintf("Version %s, %s required", currentCopy.Spec.Version, desiredCopy.Spec.Version)) } // Note: we return a message based on desired.bootstrap.ConfigRef != nil, but we always compare the entire bootstrap @@ -394,133 +378,162 @@ func MachineTemplateUpToDate(current, desired *clusterv1.MachineTemplateSpec) (u // common operation so it is acceptable to handle it in this way). if currentCopy.Spec.Bootstrap.ConfigRef.IsDefined() { if !reflect.DeepEqual(currentCopy.Spec.Bootstrap, desiredCopy.Spec.Bootstrap) { - logMessages = append(logMessages, fmt.Sprintf("spec.bootstrap.configRef %s %s, %s %s required", currentCopy.Spec.Bootstrap.ConfigRef.Kind, currentCopy.Spec.Bootstrap.ConfigRef.Name, desiredCopy.Spec.Bootstrap.ConfigRef.Kind, desiredCopy.Spec.Bootstrap.ConfigRef.Name)) + res.LogMessages = append(res.LogMessages, fmt.Sprintf("spec.bootstrap.configRef %s %s, %s %s required", currentCopy.Spec.Bootstrap.ConfigRef.Kind, currentCopy.Spec.Bootstrap.ConfigRef.Name, desiredCopy.Spec.Bootstrap.ConfigRef.Kind, desiredCopy.Spec.Bootstrap.ConfigRef.Name)) // Note: dropping "Template" suffix because conditions message will surface on machine. - conditionMessages = append(conditionMessages, fmt.Sprintf("%s is not up-to-date", strings.TrimSuffix(currentCopy.Spec.Bootstrap.ConfigRef.Kind, clusterv1.TemplateSuffix))) + res.ConditionMessages = append(res.ConditionMessages, fmt.Sprintf("%s is not up-to-date", strings.TrimSuffix(currentCopy.Spec.Bootstrap.ConfigRef.Kind, clusterv1.TemplateSuffix))) } } else { if !reflect.DeepEqual(currentCopy.Spec.Bootstrap, desiredCopy.Spec.Bootstrap) { - logMessages = append(logMessages, fmt.Sprintf("spec.bootstrap.dataSecretName %s, %s required", ptr.Deref(currentCopy.Spec.Bootstrap.DataSecretName, "nil"), ptr.Deref(desiredCopy.Spec.Bootstrap.DataSecretName, "nil"))) - conditionMessages = append(conditionMessages, fmt.Sprintf("spec.bootstrap.dataSecretName %s, %s required", ptr.Deref(currentCopy.Spec.Bootstrap.DataSecretName, "nil"), ptr.Deref(desiredCopy.Spec.Bootstrap.DataSecretName, "nil"))) + res.LogMessages = append(res.LogMessages, fmt.Sprintf("spec.bootstrap.dataSecretName %s, %s required", ptr.Deref(currentCopy.Spec.Bootstrap.DataSecretName, "nil"), ptr.Deref(desiredCopy.Spec.Bootstrap.DataSecretName, "nil"))) + res.ConditionMessages = append(res.ConditionMessages, fmt.Sprintf("spec.bootstrap.dataSecretName %s, %s required", ptr.Deref(currentCopy.Spec.Bootstrap.DataSecretName, "nil"), ptr.Deref(desiredCopy.Spec.Bootstrap.DataSecretName, "nil"))) } } if !reflect.DeepEqual(currentCopy.Spec.InfrastructureRef, desiredCopy.Spec.InfrastructureRef) { - logMessages = append(logMessages, fmt.Sprintf("spec.infrastructureRef %s %s, %s %s required", currentCopy.Spec.InfrastructureRef.Kind, currentCopy.Spec.InfrastructureRef.Name, desiredCopy.Spec.InfrastructureRef.Kind, desiredCopy.Spec.InfrastructureRef.Name)) + res.LogMessages = append(res.LogMessages, fmt.Sprintf("spec.infrastructureRef %s %s, %s %s required", currentCopy.Spec.InfrastructureRef.Kind, currentCopy.Spec.InfrastructureRef.Name, desiredCopy.Spec.InfrastructureRef.Kind, desiredCopy.Spec.InfrastructureRef.Name)) // Note: dropping "Template" suffix because conditions message will surface on machine. - conditionMessages = append(conditionMessages, fmt.Sprintf("%s is not up-to-date", strings.TrimSuffix(currentCopy.Spec.InfrastructureRef.Kind, clusterv1.TemplateSuffix))) + res.ConditionMessages = append(res.ConditionMessages, fmt.Sprintf("%s is not up-to-date", strings.TrimSuffix(currentCopy.Spec.InfrastructureRef.Kind, clusterv1.TemplateSuffix))) } if currentCopy.Spec.FailureDomain != desiredCopy.Spec.FailureDomain { - logMessages = append(logMessages, fmt.Sprintf("spec.failureDomain %s, %s required", currentCopy.Spec.FailureDomain, desiredCopy.Spec.FailureDomain)) - conditionMessages = append(conditionMessages, fmt.Sprintf("Failure domain %s, %s required", currentCopy.Spec.FailureDomain, desiredCopy.Spec.FailureDomain)) + res.LogMessages = append(res.LogMessages, fmt.Sprintf("spec.failureDomain %s, %s required", currentCopy.Spec.FailureDomain, desiredCopy.Spec.FailureDomain)) + res.ConditionMessages = append(res.ConditionMessages, fmt.Sprintf("Failure domain %s, %s required", currentCopy.Spec.FailureDomain, desiredCopy.Spec.FailureDomain)) } - if len(logMessages) > 0 || len(conditionMessages) > 0 { - return false, logMessages, conditionMessages + if len(res.LogMessages) > 0 || len(res.ConditionMessages) > 0 { + return false, res } - return true, nil, nil + // Machine is up to date, no need for in-place update. + res.EligibleForInPlaceUpdate = false + return true, res } // MachineTemplateDeepCopyRolloutFields copies a MachineTemplateSpec // and sets all fields that should be propagated in-place to nil and drops version from // external references. +// Note: Please update inplace.CleanupMachineSpecForDiff accordingly if necessary. func MachineTemplateDeepCopyRolloutFields(template *clusterv1.MachineTemplateSpec) *clusterv1.MachineTemplateSpec { templateCopy := template.DeepCopy() + spec := templateCopy.Spec - // Moving MD from one cluster to another is not supported. - templateCopy.Spec.ClusterName = "" + // The following fields are set to their zero value so they are omitted from the comparison, + // because they should never be the reason for a rollout. // Drop labels and annotations templateCopy.Labels = nil templateCopy.Annotations = nil - // Drop node timeout values - templateCopy.Spec.MinReadySeconds = nil - templateCopy.Spec.ReadinessGates = nil - templateCopy.Spec.Deletion.NodeDrainTimeoutSeconds = nil - templateCopy.Spec.Deletion.NodeDeletionTimeoutSeconds = nil - templateCopy.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = nil + // Should never change. + spec.ClusterName = "" + + // Bootstrap and InfrastructureRef should be compared. + + // Should not be set. + spec.ProviderID = "" + + // Version & FailureDomain should be compared. + + // Fields that are mutated in-place without a rollout. + spec.MinReadySeconds = nil + spec.ReadinessGates = nil + spec.Deletion.NodeDrainTimeoutSeconds = nil + spec.Deletion.NodeVolumeDetachTimeoutSeconds = nil + spec.Deletion.NodeDeletionTimeoutSeconds = nil + spec.Taints = nil return templateCopy } -// FindNewMachineSet returns the new MS this given deployment targets (the one with the same machine template, ignoring -// in-place mutable fields). +// FindNewAndOldMachineSets returns the newMS for a MachineDeployment (the one with the same machine template, ignoring +// in-place mutable fields) as well as return oldMSs. // Note: If the reconciliation time is after the deployment's `rolloutAfter` time, a MS has to be newer than // `rolloutAfter` to be considered as matching the deployment's intent. // NOTE: If we find a matching MachineSet which only differs in in-place mutable fields we can use it to // fulfill the intent of the MachineDeployment by just updating the MachineSet to propagate in-place mutable fields. // Thus we don't have to create a new MachineSet and we can avoid an unnecessary rollout. -// NOTE: Even after we changed MachineTemplateUpToDate to ignore fields that are propagated in-place we can guarantee that if there exists a "new machineset" -// using the old logic then a new machineset will definitely exist using the new logic. The new logic is looser. Therefore, we will -// not face a case where there exists a machine set matching the old logic but there does not exist a machineset matching the new logic. -// In fact previously not matching MS can now start matching the target. Since there could be multiple matches, lets choose the -// MS with the most replicas so that there is minimum machine churn. -func FindNewMachineSet(deployment *clusterv1.MachineDeployment, msList []*clusterv1.MachineSet, reconciliationTime *metav1.Time) (*clusterv1.MachineSet, string, error) { +func FindNewAndOldMachineSets(deployment *clusterv1.MachineDeployment, msList []*clusterv1.MachineSet, reconciliationTime metav1.Time) (newMS *clusterv1.MachineSet, oldMSs []*clusterv1.MachineSet, upToDateResults map[string]UpToDateResult, createReason string) { if len(msList) == 0 { - return nil, "no MachineSets exist for the MachineDeployment", nil + return nil, nil, nil, "no MachineSets exist for the MachineDeployment" } - // In rare cases, such as after cluster upgrades, Deployment may end up with - // having more than one new MachineSets that have the same template, - // see https://github.com/kubernetes/kubernetes/issues/40415 - // We deterministically choose the oldest new MachineSet with matching template hash. + // It could happen that there is more than one newMS candidate when reconciliationTime is > rolloutAfter; considering this, the + // current implementation treats candidates that will be discarded as old machine sets not eligible for in-place updates. + // NOTE: We could also have more than one MS candidate in the very unlikely case where + // the diff logic MachineTemplateUpToDate is changed by dropping one of the existing criteria (version, failureDomain, infra/BootstrapRef). + // NOTE: When dealing with more than one newMS candidate, deterministically choose the MS with the most replicas + // so that there is minimum machine churn. + var newMSCandidates []*clusterv1.MachineSet sort.Sort(MachineSetsByDecreasingReplicas(msList)) - var matchingMachineSets []*clusterv1.MachineSet + oldMSs = make([]*clusterv1.MachineSet, 0) + upToDateResults = make(map[string]UpToDateResult) var diffs []string for _, ms := range msList { - upToDate, logMessages, _ := MachineTemplateUpToDate(&ms.Spec.Template, &deployment.Spec.Template) + upToDate, upToDateResult := MachineTemplateUpToDate(&ms.Spec.Template, &deployment.Spec.Template) + upToDateResults[ms.Name] = upToDateResult if upToDate { - matchingMachineSets = append(matchingMachineSets, ms) + newMSCandidates = append(newMSCandidates, ms) } else { - diffs = append(diffs, fmt.Sprintf("MachineSet %s: diff: %s", ms.Name, strings.Join(logMessages, ", "))) + oldMSs = append(oldMSs, ms) + // Override the EligibleForInPlaceUpdate decision if rollout after is expired. + if !deployment.Spec.Rollout.After.IsZero() && deployment.Spec.Rollout.After.Before(&reconciliationTime) && ms.CreationTimestamp.Before(ptr.To(deployment.Spec.Rollout.After)) { + upToDateResult.EligibleForInPlaceUpdate = false + upToDateResult.LogMessages = append(upToDateResult.LogMessages, "MachineDeployment spec.rolloutAfter expired") + // No need to set an additional condition message, it is not used anywhere. + upToDateResults[ms.Name] = upToDateResult + } + diffs = append(diffs, fmt.Sprintf("MachineSet %s needs rollout: %s", ms.Name, strings.Join(upToDateResult.LogMessages, ", "))) } } - if len(matchingMachineSets) == 0 { - return nil, fmt.Sprintf("couldn't find MachineSet matching MachineDeployment spec template: %s", strings.Join(diffs, "; ")), nil + if len(newMSCandidates) == 0 { + return nil, oldMSs, upToDateResults, fmt.Sprintf("couldn't find MachineSet matching MachineDeployment spec template: %s", strings.Join(diffs, "; ")) } // If RolloutAfter is not set, pick the first matching MachineSet. if deployment.Spec.Rollout.After.IsZero() { - return matchingMachineSets[0], "", nil + for _, ms := range newMSCandidates[1:] { + oldMSs = append(oldMSs, ms) + upToDateResults[ms.Name] = UpToDateResult{ + // No need to set log or condition message for discarded candidates, it is not used anywhere. + EligibleForInPlaceUpdate: false, + } + } + return newMSCandidates[0], oldMSs, upToDateResults, "" } // If reconciliation time is before RolloutAfter, pick the first matching MachineSet. if reconciliationTime.Before(&deployment.Spec.Rollout.After) { - return matchingMachineSets[0], "", nil + for _, ms := range newMSCandidates[1:] { + oldMSs = append(oldMSs, ms) + upToDateResults[ms.Name] = UpToDateResult{ + // No need to set log or condition for discarded candidates, it is not used anywhere. + EligibleForInPlaceUpdate: false, + } + } + return newMSCandidates[0], oldMSs, upToDateResults, "" } // Pick the first matching MachineSet that has been created at RolloutAfter or later. - for _, ms := range matchingMachineSets { - if ms.CreationTimestamp.Sub(deployment.Spec.Rollout.After.Time) >= 0 { - return ms, "", nil + for _, ms := range newMSCandidates { + if newMS == nil && !ms.CreationTimestamp.Before(ptr.To(deployment.Spec.Rollout.After)) { + newMS = ms + continue + } + + oldMSs = append(oldMSs, ms) + upToDateResults[ms.Name] = UpToDateResult{ + // No need to set log or condition for discarded candidates, it is not used anywhere. + EligibleForInPlaceUpdate: false, } } // If no matching MachineSet was created after RolloutAfter, trigger creation of a new MachineSet. - return nil, fmt.Sprintf("spec.rollout.after on MachineDeployment set to %s, no MachineSet has been created afterwards", deployment.Spec.Rollout.After.Format(time.RFC3339)), nil -} - -// FindOldMachineSets returns the old machine sets targeted by the given Deployment, within the given slice of MSes. -// Returns a list of machine sets which contains all old machine sets. -func FindOldMachineSets(deployment *clusterv1.MachineDeployment, msList []*clusterv1.MachineSet, reconciliationTime *metav1.Time) ([]*clusterv1.MachineSet, error) { - allMSs := make([]*clusterv1.MachineSet, 0, len(msList)) - newMS, _, err := FindNewMachineSet(deployment, msList, reconciliationTime) - if err != nil { - return nil, err + if newMS == nil { + return nil, oldMSs, upToDateResults, fmt.Sprintf("spec.rollout.after on MachineDeployment set to %s, no MachineSet has been created afterwards", deployment.Spec.Rollout.After.Format(time.RFC3339)) } - for _, ms := range msList { - // Filter out new machine set - if newMS != nil && ms.UID == newMS.UID { - continue - } - allMSs = append(allMSs, ms) - } - return allMSs, nil + return newMS, oldMSs, upToDateResults, "" } // GetReplicaCountForMachineSets returns the sum of Replicas of the given machine sets. @@ -649,39 +662,39 @@ func DeploymentComplete(deployment *clusterv1.MachineDeployment, newStatus *clus // 1) The new MS is saturated: newMS's replicas == deployment's replicas // 2) For RollingUpdateStrategy: Max number of machines allowed is reached: deployment's replicas + maxSurge == all MSs' replicas. // 3) For OnDeleteStrategy: Max number of machines allowed is reached: deployment's replicas == all MSs' replicas. -func NewMSNewReplicas(deployment *clusterv1.MachineDeployment, allMSs []*clusterv1.MachineSet, newMSReplicas int32) (int32, error) { +func NewMSNewReplicas(deployment *clusterv1.MachineDeployment, allMSs []*clusterv1.MachineSet, newMSReplicas int32) (int32, string, error) { switch deployment.Spec.Rollout.Strategy.Type { case clusterv1.RollingUpdateMachineDeploymentStrategyType: // Check if we can scale up. maxSurge, err := intstrutil.GetScaledValueFromIntOrPercent(deployment.Spec.Rollout.Strategy.RollingUpdate.MaxSurge, int(*(deployment.Spec.Replicas)), true) if err != nil { - return 0, err + return 0, "", err } // Find the total number of machines currentMachineCount := TotalMachineSetsReplicaSum(allMSs) maxTotalMachines := *(deployment.Spec.Replicas) + int32(maxSurge) if currentMachineCount >= maxTotalMachines { // Cannot scale up. - return newMSReplicas, nil + return newMSReplicas, fmt.Sprintf("%d current Machines >= %d MachineDeployment spec.replicas + %d maxSurge", currentMachineCount, ptr.Deref(deployment.Spec.Replicas, 0), maxSurge), nil } // Scale up. scaleUpCount := maxTotalMachines - currentMachineCount // Do not exceed the number of desired replicas. scaleUpCount = min(scaleUpCount, *(deployment.Spec.Replicas)-newMSReplicas) - return newMSReplicas + scaleUpCount, nil + return newMSReplicas + scaleUpCount, fmt.Sprintf("%d current Machines < %d MachineDeployment spec.replicas + %d maxSurge", currentMachineCount, ptr.Deref(deployment.Spec.Replicas, 0), maxSurge), nil case clusterv1.OnDeleteMachineDeploymentStrategyType: // Find the total number of machines currentMachineCount := TotalMachineSetsReplicaSum(allMSs) if currentMachineCount >= *(deployment.Spec.Replicas) { // Cannot scale up as more replicas exist than desired number of replicas in the MachineDeployment. - return newMSReplicas, nil + return newMSReplicas, fmt.Sprintf("%d current Machines >= %d MachineDeployment spec.replicas", currentMachineCount, ptr.Deref(deployment.Spec.Replicas, 0)), nil } // Scale up the latest MachineSet so the total amount of replicas across all MachineSets match // the desired number of replicas in the MachineDeployment scaleUpCount := *(deployment.Spec.Replicas) - currentMachineCount - return newMSReplicas + scaleUpCount, nil + return newMSReplicas + scaleUpCount, fmt.Sprintf("%d current Machines < %d MachineDeployment spec.replicas", currentMachineCount, ptr.Deref(deployment.Spec.Replicas, 0)), nil default: - return 0, fmt.Errorf("failed to compute replicas: deployment strategy %v isn't supported", deployment.Spec.Rollout.Strategy.Type) + return 0, "", fmt.Errorf("failed to compute replicas: deployment strategy %v isn't supported", deployment.Spec.Rollout.Strategy.Type) } } @@ -810,15 +823,3 @@ func CloneSelectorAndAddLabel(selector *metav1.LabelSelector, labelKey, labelVal return newSelector } - -// GetDeletingMachineCount gets the number of machines that are in the process of being deleted -// in a machineList. -func GetDeletingMachineCount(machineList *clusterv1.MachineList) int32 { - var deletingMachineCount int32 - for _, machine := range machineList.Items { - if !machine.GetDeletionTimestamp().IsZero() { - deletingMachineCount++ - } - } - return deletingMachineCount -} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinehealthcheck/machinehealthcheck_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinehealthcheck/machinehealthcheck_controller.go index b92ce67940..738dcef647 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinehealthcheck/machinehealthcheck_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinehealthcheck/machinehealthcheck_controller.go @@ -37,7 +37,6 @@ import ( "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -49,6 +48,7 @@ import ( "sigs.k8s.io/cluster-api/controllers/clustercache" "sigs.k8s.io/cluster-api/controllers/external" "sigs.k8s.io/cluster-api/internal/controllers/machine" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/conditions" @@ -100,26 +100,20 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt } r.predicateLog = ptr.To(ctrl.LoggerFrom(ctx).WithValues("controller", "machinehealthcheck")) - c, err := ctrl.NewControllerManagedBy(mgr). + c, err := capicontrollerutil.NewControllerManagedBy(mgr, *r.predicateLog). For(&clusterv1.MachineHealthCheck{}). Watches( &clusterv1.Machine{}, handler.EnqueueRequestsFromMapFunc(r.machineToMachineHealthCheck), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), *r.predicateLog)), ). WithOptions(options). WithEventFilter(predicates.ResourceHasFilterLabel(mgr.GetScheme(), *r.predicateLog, r.WatchFilterValue)). Watches( &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(r.clusterToMachineHealthCheck), - builder.WithPredicates( - // TODO: should this wait for Cluster.Status.InfrastructureReady similar to Infra Machine resources? - predicates.All(mgr.GetScheme(), *r.predicateLog, - predicates.ResourceIsChanged(mgr.GetScheme(), *r.predicateLog), - predicates.ClusterPausedTransitions(mgr.GetScheme(), *r.predicateLog), - predicates.ResourceHasFilterLabel(mgr.GetScheme(), *r.predicateLog, r.WatchFilterValue), - ), - ), + // TODO: should this wait for Cluster.Status.InfrastructureReady similar to Infra Machine resources? + predicates.ClusterPausedTransitions(mgr.GetScheme(), *r.predicateLog), + predicates.ResourceHasFilterLabel(mgr.GetScheme(), *r.predicateLog, r.WatchFilterValue), ). WatchesRawSource(r.ClusterCache.GetClusterSource("machinehealthcheck", r.clusterToMachineHealthCheck)). Build(r) @@ -211,7 +205,6 @@ func (r *Reconciler) reconcile(ctx context.Context, logger logr.Logger, cluster var err error remoteClient, err = r.ClusterCache.GetClient(ctx, util.ObjectKey(cluster)) if err != nil { - logger.Error(err, "Error creating remote cluster cache") return ctrl.Result{}, err } @@ -224,8 +217,7 @@ func (r *Reconciler) reconcile(ctx context.Context, logger logr.Logger, cluster logger.V(3).Info("Finding targets") targets, err := r.getTargetsFromMHC(ctx, logger, remoteClient, cluster, m) if err != nil { - logger.Error(err, "Failed to fetch targets from MachineHealthCheck") - return ctrl.Result{}, err + return ctrl.Result{}, errors.Wrapf(err, "failed to fetch targets from MachineHealthCheck") } totalTargets := len(targets) m.Status.ExpectedMachines = ptr.To(int32(totalTargets)) @@ -326,7 +318,7 @@ func (r *Reconciler) reconcile(ctx context.Context, logger logr.Logger, cluster if len(errList) > 0 { return ctrl.Result{}, kerrors.NewAggregate(errList) } - return reconcile.Result{Requeue: true}, nil + return reconcile.Result{}, nil } if m.Spec.Remediation.TriggerIf.UnhealthyInRange == "" { diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinehealthcheck/machinehealthcheck_targets.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinehealthcheck/machinehealthcheck_targets.go index f80820279b..b2e08ed8c1 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinehealthcheck/machinehealthcheck_targets.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinehealthcheck/machinehealthcheck_targets.go @@ -19,6 +19,7 @@ package machinehealthcheck import ( "context" "fmt" + "strings" "time" "github.com/go-logr/logr" @@ -44,9 +45,6 @@ const ( // EventMachineMarkedUnhealthy is emitted when machine was successfully marked as unhealthy. EventMachineMarkedUnhealthy string = "MachineMarkedUnhealthy" - // EventDetectedUnhealthy is emitted in case a node associated with a - // machine was detected unhealthy. - EventDetectedUnhealthy string = "DetectedUnhealthy" ) var ( @@ -65,28 +63,21 @@ type healthCheckTarget struct { nodeMissing bool } -// Get the node name if the target has a node. -func (t *healthCheckTarget) nodeName() string { - if t.Node != nil { - return t.Node.GetName() - } - return "" -} - -// Determine whether or not a given target needs remediation. -// The node will need remediation if any of the following are true: +// needsRemediation determines whether a given target needs remediation. +// The machine will need remediation if any of the following are true: // - The Machine has the remediate machine annotation -// - The Machine has failed for some reason +// - Any condition on the machine matches the configured checks and exceeds the timeout // - The Machine did not get a node before `timeoutForMachineToHaveNode` elapses -// - The Node has gone away -// - Any condition on the node is matched for the given timeout -// If the target doesn't currently need rememdiation, provide a duration after -// which the target should next be checked. -// The target should be requeued after this duration. +// - The Node has been deleted but the Machine still references it +// - Any condition on the node matches the configured checks and exceeds the timeout +// +// Machine conditions are always evaluated first and consistently across all scenarios +// (node missing, node startup timeout, node exists) to ensure comprehensive health checking. +// When multiple issues are detected, error messages are merged to provide complete visibility. +// +// Returns true if remediation is needed, and a duration indicating when to recheck if remediation +// is not immediately required. The target should be requeued after this duration. func (t *healthCheckTarget) needsRemediation(logger logr.Logger, timeoutForMachineToHaveNode metav1.Duration) (bool, time.Duration) { - var nextCheckTimes []time.Duration - now := time.Now() - if annotations.HasRemediateMachine(t.Machine) { v1beta1conditions.MarkFalse(t.Machine, clusterv1.MachineHealthCheckSucceededV1Beta1Condition, clusterv1.HasRemediateMachineAnnotationV1Beta1Reason, clusterv1.ConditionSeverityWarning, "Marked for remediation via remediate-machine annotation") logger.V(3).Info("Target is marked for remediation via remediate-machine annotation") @@ -100,20 +91,6 @@ func (t *healthCheckTarget) needsRemediation(logger logr.Logger, timeoutForMachi return true, time.Duration(0) } - // Machine has Status.NodeRef set, although we couldn't find the node in the workload cluster. - if t.nodeMissing { - logger.V(3).Info("Target is unhealthy: node is missing") - v1beta1conditions.MarkFalse(t.Machine, clusterv1.MachineHealthCheckSucceededV1Beta1Condition, clusterv1.NodeNotFoundV1Beta1Reason, clusterv1.ConditionSeverityWarning, "") - - conditions.Set(t.Machine, metav1.Condition{ - Type: clusterv1.MachineHealthCheckSucceededCondition, - Status: metav1.ConditionFalse, - Reason: clusterv1.MachineHealthCheckNodeDeletedReason, - Message: fmt.Sprintf("Health check failed: Node %s has been deleted", t.Machine.Status.NodeRef.Name), - }) - return true, time.Duration(0) - } - // Don't penalize any Machine/Node if the control plane has not been initialized // Exception of this rule are control plane machine itself, so the first control plane machine can be remediated. if !conditions.IsTrue(t.Cluster, clusterv1.ClusterControlPlaneInitializedCondition) && !util.IsControlPlaneMachine(t.Machine) { @@ -129,12 +106,120 @@ func (t *healthCheckTarget) needsRemediation(logger logr.Logger, timeoutForMachi return false, 0 } + // Check machine conditions + unhealthyMachineMessages, nextMachineCheck := t.machineChecks(logger) + + // Check node conditions + nodeConditionReason, nodeV1beta1ConditionReason, unhealthyNodeMessages, nextNodeCheck := t.nodeChecks(logger, timeoutForMachineToHaveNode) + + // Combine results + if len(unhealthyMachineMessages) == 0 && len(unhealthyNodeMessages) == 0 { + var nextCheckTimes []time.Duration + if nextMachineCheck > 0 { + nextCheckTimes = append(nextCheckTimes, nextMachineCheck) + } + if nextNodeCheck > 0 { + nextCheckTimes = append(nextCheckTimes, nextNodeCheck) + } + result := minDuration(nextCheckTimes) + return false, result + } + + reason := nodeConditionReason + v1beta1Reason := nodeV1beta1ConditionReason + if len(unhealthyMachineMessages) > 0 { + reason = clusterv1.MachineHealthCheckUnhealthyMachineReason + v1beta1Reason = clusterv1.UnhealthyMachineConditionV1Beta1Reason + } + + // Combine all messages into a single comprehensive message + allMessages := append(unhealthyMachineMessages, unhealthyNodeMessages...) + + conditionMessage := "Health check failed:\n" + for i, m := range allMessages { + conditionMessage += fmt.Sprintf(" * %s", m) + if i != len(allMessages)-1 { + conditionMessage += "\n" + } + } + + conditions.Set(t.Machine, metav1.Condition{ + Type: clusterv1.MachineHealthCheckSucceededCondition, + Status: metav1.ConditionFalse, + Reason: reason, + Message: conditionMessage, + }) + + // v1beta1 condition message formatting: + // - For NodeNotFound (node deleted), the legacy condition historically has an empty message. + // - For all other reasons (e.g., NodeStartupTimeout, UnhealthyNodeCondition, UnhealthyMachineCondition), + // include a concise, semicolon-separated list of messages without the "Health check failed" prefix. + v1beta1Message := "" + if v1beta1Reason != clusterv1.NodeNotFoundV1Beta1Reason { + v1beta1Message = strings.Join(allMessages, "; ") + } + v1beta1conditions.MarkFalse(t.Machine, clusterv1.MachineHealthCheckSucceededV1Beta1Condition, v1beta1Reason, clusterv1.ConditionSeverityWarning, "%s", v1beta1Message) + + return true, time.Duration(0) +} + +func (t *healthCheckTarget) machineChecks(logger logr.Logger) ([]string, time.Duration) { + var unhealthyMachineMessages []string + var nextCheckTimes []time.Duration + now := time.Now() + + // Always check machine conditions first, regardless of node state + for _, c := range t.MHC.Spec.Checks.UnhealthyMachineConditions { + machineCondition := getMachineCondition(t.Machine, c.Type) + + // Skip when current machine condition is different from the one reported + // in the MachineHealthCheck. + if machineCondition == nil || machineCondition.Status != c.Status { + continue + } + + // If the machine condition has been in the unhealthy state for longer than the + // timeout, mark as unhealthy and collect the message. + timeoutSecondsDuration := time.Duration(ptr.Deref(c.TimeoutSeconds, 0)) * time.Second + + if machineCondition.LastTransitionTime.Add(timeoutSecondsDuration).Before(now) { + unhealthyMachineMessages = append(unhealthyMachineMessages, fmt.Sprintf("Condition %s on Machine is reporting status %s with reason %s for more than %s", + c.Type, c.Status, machineCondition.Reason, timeoutSecondsDuration.String())) + logger.V(3).Info(fmt.Sprintf("Target is unhealthy: Machine condition is in unhealthy state more than %s", timeoutSecondsDuration.String()), + "condition", c.Type, "state", c.Status, "reason", machineCondition.Reason, "message", machineCondition.Message) + continue + } + + durationUnhealthy := now.Sub(machineCondition.LastTransitionTime.Time) + nextCheck := timeoutSecondsDuration - durationUnhealthy + time.Second + if nextCheck > 0 { + nextCheckTimes = append(nextCheckTimes, nextCheck) + } + } + + if len(unhealthyMachineMessages) > 0 { + return unhealthyMachineMessages, time.Duration(0) + } + return nil, minDuration(nextCheckTimes) +} + +func (t *healthCheckTarget) nodeChecks(logger logr.Logger, timeoutForMachineToHaveNode metav1.Duration) (string, string, []string, time.Duration) { + var nextCheckTimes []time.Duration + now := time.Now() + + // Machine has Status.NodeRef set, although we couldn't find the node in the workload cluster. + if t.nodeMissing { + logger.V(3).Info("Target is unhealthy: node is missing") + nodeMissingMessage := fmt.Sprintf("Node %s has been deleted", t.Machine.Status.NodeRef.Name) + return clusterv1.MachineHealthCheckNodeDeletedReason, clusterv1.NodeNotFoundV1Beta1Reason, []string{nodeMissingMessage}, time.Duration(0) + } + // the node has not been set yet if t.Node == nil { if timeoutForMachineToHaveNode == disabledNodeStartupTimeout { // Startup timeout is disabled so no need to go any further. // No node yet to check conditions, can return early here. - return false, 0 + return "", "", nil, time.Duration(0) } controlPlaneInitialized := conditions.GetLastTransitionTime(t.Cluster, clusterv1.ClusterControlPlaneInitializedCondition) @@ -163,25 +248,18 @@ func (t *healthCheckTarget) needsRemediation(logger logr.Logger, timeoutForMachi timeoutDuration := timeoutForMachineToHaveNode.Duration if comparisonTime.Add(timeoutForMachineToHaveNode.Duration).Before(now) { - v1beta1conditions.MarkFalse(t.Machine, clusterv1.MachineHealthCheckSucceededV1Beta1Condition, clusterv1.NodeStartupTimeoutV1Beta1Reason, clusterv1.ConditionSeverityWarning, "Node failed to report startup in %s", timeoutDuration) logger.V(3).Info("Target is unhealthy: machine has no node", "duration", timeoutDuration) - - conditions.Set(t.Machine, metav1.Condition{ - Type: clusterv1.MachineHealthCheckSucceededCondition, - Status: metav1.ConditionFalse, - Reason: clusterv1.MachineHealthCheckNodeStartupTimeoutReason, - Message: fmt.Sprintf("Health check failed: Node failed to report startup in %s", timeoutDuration), - }) - return true, time.Duration(0) + nodeTimeoutMessage := fmt.Sprintf("Node failed to report startup in %s", timeoutDuration) + return clusterv1.MachineHealthCheckNodeStartupTimeoutReason, clusterv1.NodeStartupTimeoutV1Beta1Reason, []string{nodeTimeoutMessage}, time.Duration(0) } durationUnhealthy := now.Sub(comparisonTime) nextCheck := timeoutDuration - durationUnhealthy + time.Second - - return false, nextCheck + return "", "", nil, nextCheck } - // check conditions + // check node conditions (only when node is available) + var unhealthyNodeMessages []string for _, c := range t.MHC.Spec.Checks.UnhealthyNodeConditions { nodeCondition := getNodeCondition(t.Node, c.Type) @@ -191,21 +269,16 @@ func (t *healthCheckTarget) needsRemediation(logger logr.Logger, timeoutForMachi continue } - // If the condition has been in the unhealthy state for longer than the - // timeout, return true with no requeue time. + // If the node condition has been in the unhealthy state for longer than the + // timeout, mark as unhealthy and collect the message. timeoutSecondsDuration := time.Duration(ptr.Deref(c.TimeoutSeconds, 0)) * time.Second if nodeCondition.LastTransitionTime.Add(timeoutSecondsDuration).Before(now) { - v1beta1conditions.MarkFalse(t.Machine, clusterv1.MachineHealthCheckSucceededV1Beta1Condition, clusterv1.UnhealthyNodeConditionV1Beta1Reason, clusterv1.ConditionSeverityWarning, "Condition %s on node is reporting status %s for more than %s", c.Type, c.Status, timeoutSecondsDuration.String()) - logger.V(3).Info("Target is unhealthy: condition is in state longer than allowed timeout", "condition", c.Type, "state", c.Status, "timeout", timeoutSecondsDuration.String()) - - conditions.Set(t.Machine, metav1.Condition{ - Type: clusterv1.MachineHealthCheckSucceededCondition, - Status: metav1.ConditionFalse, - Reason: clusterv1.MachineHealthCheckUnhealthyNodeReason, - Message: fmt.Sprintf("Health check failed: Condition %s on Node is reporting status %s for more than %s", c.Type, c.Status, timeoutSecondsDuration.String()), - }) - return true, time.Duration(0) + unhealthyNodeMessages = append(unhealthyNodeMessages, fmt.Sprintf("Condition %s on Node is reporting status %s with reason %s for more than %s", + c.Type, c.Status, nodeCondition.Reason, timeoutSecondsDuration.String())) + logger.V(3).Info(fmt.Sprintf("Target is unhealthy: Node condition is in unhealthy state more than %s", timeoutSecondsDuration.String()), + "condition", c.Type, "state", c.Status, "reason", nodeCondition.Reason, "message", nodeCondition.Message) + continue } durationUnhealthy := now.Sub(nodeCondition.LastTransitionTime.Time) @@ -214,7 +287,11 @@ func (t *healthCheckTarget) needsRemediation(logger logr.Logger, timeoutForMachi nextCheckTimes = append(nextCheckTimes, nextCheck) } } - return false, minDuration(nextCheckTimes) + + if len(unhealthyNodeMessages) > 0 { + return clusterv1.MachineHealthCheckUnhealthyNodeReason, clusterv1.UnhealthyNodeConditionV1Beta1Reason, unhealthyNodeMessages, time.Duration(0) + } + return "", "", nil, minDuration(nextCheckTimes) } // getTargetsFromMHC uses the MachineHealthCheck's selector to fetch machines @@ -323,15 +400,7 @@ func (r *Reconciler) healthCheckTargets(targets []healthCheckTarget, logger logr } if nextCheck > 0 { - logger.V(3).Info("Target is likely to go unhealthy", "timeUntilUnhealthy", nextCheck.Truncate(time.Second).String()) - r.recorder.Eventf( - t.Machine, - corev1.EventTypeNormal, - EventDetectedUnhealthy, - "Machine %s has unhealthy Node %s", - klog.KObj(t.Machine), - t.nodeName(), - ) + logger.V(2).Info("Target is likely to go unhealthy", "timeUntilUnhealthy", nextCheck.Truncate(time.Second).String()) nextCheckTimes = append(nextCheckTimes, nextCheck) continue } @@ -360,6 +429,16 @@ func getNodeCondition(node *corev1.Node, conditionType corev1.NodeConditionType) return nil } +// getMachineCondition returns machine condition by type. +func getMachineCondition(node *clusterv1.Machine, conditionType string) *metav1.Condition { + for _, cond := range node.Status.Conditions { + if cond.Type == conditionType { + return &cond + } + } + return nil +} + func minDuration(durations []time.Duration) time.Duration { if len(durations) == 0 { return time.Duration(0) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/OWNERS b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/OWNERS new file mode 100644 index 0000000000..446badddfc --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/OWNERS @@ -0,0 +1,8 @@ +# See the OWNERS docs at https://go.k8s.io/owners + +approvers: + - cluster-api-machinepool-maintainers + +reviewers: + - cluster-api-reviewers + - cluster-api-machinepool-reviewers diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/controllers/doc.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/doc.go similarity index 87% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/controllers/doc.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/doc.go index a58a55ea32..f69e347c6e 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/controllers/doc.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/doc.go @@ -14,5 +14,5 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package controllers implements experimental controllers. -package controllers +// Package machinepool implements the MachinePool controller. +package machinepool diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller.go similarity index 90% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller.go index cc72d98578..5eb6f6dc53 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package machinepool import ( "context" @@ -32,7 +32,6 @@ import ( "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -44,6 +43,7 @@ import ( "sigs.k8s.io/cluster-api/api/core/v1beta2/index" "sigs.k8s.io/cluster-api/controllers/clustercache" "sigs.k8s.io/cluster-api/controllers/external" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/internal/util/ssa" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/conditions" @@ -71,8 +71,8 @@ const ( MachinePoolControllerName = "machinepool-controller" ) -// MachinePoolReconciler reconciles a MachinePool object. -type MachinePoolReconciler struct { +// Reconciler reconciles a MachinePool object. +type Reconciler struct { Client client.Client APIReader client.Reader ClusterCache clustercache.ClusterCache @@ -88,7 +88,7 @@ type MachinePoolReconciler struct { predicateLog *logr.Logger } -func (r *MachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { +func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { if r.Client == nil || r.APIReader == nil || r.ClusterCache == nil { return errors.New("Client, APIReader and ClusterCache must not be nil") } @@ -99,7 +99,7 @@ func (r *MachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.M return err } - c, err := ctrl.NewControllerManagedBy(mgr). + c, err := capicontrollerutil.NewControllerManagedBy(mgr, *r.predicateLog). For(&clusterv1.MachinePool{}). WithOptions(options). WithEventFilter(predicates.ResourceHasFilterLabel(mgr.GetScheme(), *r.predicateLog, r.WatchFilterValue)). @@ -107,13 +107,8 @@ func (r *MachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.M &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(clusterToMachinePools), // TODO: should this wait for Cluster.Status.InfrastructureReady similar to Infra Machine resources? - builder.WithPredicates( - predicates.All(mgr.GetScheme(), *r.predicateLog, - predicates.ResourceIsChanged(mgr.GetScheme(), *r.predicateLog), - predicates.ClusterPausedTransitions(mgr.GetScheme(), *r.predicateLog), - predicates.ResourceHasFilterLabel(mgr.GetScheme(), *r.predicateLog, r.WatchFilterValue), - ), - ), + predicates.ClusterPausedTransitions(mgr.GetScheme(), *r.predicateLog), + predicates.ResourceHasFilterLabel(mgr.GetScheme(), *r.predicateLog, r.WatchFilterValue), ). WatchesRawSource(r.ClusterCache.GetClusterSource("machinepool", clusterToMachinePools)). Build(r) @@ -134,7 +129,7 @@ func (r *MachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.M return nil } -func (r *MachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) { +func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) { log := ctrl.LoggerFrom(ctx) mp := &clusterv1.MachinePool{} @@ -241,7 +236,7 @@ func (r *MachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) return doReconcile(ctx, scope, reconcileNormal) } -func (r *MachinePoolReconciler) reconcileSetOwnerAndLabels(_ context.Context, s *scope) (ctrl.Result, error) { +func (r *Reconciler) reconcileSetOwnerAndLabels(_ context.Context, s *scope) (ctrl.Result, error) { cluster := s.cluster mp := s.machinePool @@ -261,7 +256,7 @@ func (r *MachinePoolReconciler) reconcileSetOwnerAndLabels(_ context.Context, s } // reconcileDelete delete machinePool related resources. -func (r *MachinePoolReconciler) reconcileDelete(ctx context.Context, s *scope) (ctrl.Result, error) { +func (r *Reconciler) reconcileDelete(ctx context.Context, s *scope) (ctrl.Result, error) { if ok, err := r.reconcileDeleteExternal(ctx, s.machinePool); !ok || err != nil { // Return early and don't remove the finalizer if we got an error or // the external reconciliation deletion isn't ready. @@ -283,7 +278,7 @@ func (r *MachinePoolReconciler) reconcileDelete(ctx context.Context, s *scope) ( } // reconcileDeleteNodes delete the cluster nodes. -func (r *MachinePoolReconciler) reconcileDeleteNodes(ctx context.Context, cluster *clusterv1.Cluster, machinePool *clusterv1.MachinePool) error { +func (r *Reconciler) reconcileDeleteNodes(ctx context.Context, cluster *clusterv1.Cluster, machinePool *clusterv1.MachinePool) error { if len(machinePool.Status.NodeRefs) == 0 { return nil } @@ -297,7 +292,7 @@ func (r *MachinePoolReconciler) reconcileDeleteNodes(ctx context.Context, cluste } // isMachinePoolDeleteTimeoutPassed check the machinePool node delete time out. -func (r *MachinePoolReconciler) isMachinePoolNodeDeleteTimeoutPassed(machinePool *clusterv1.MachinePool) bool { +func (r *Reconciler) isMachinePoolNodeDeleteTimeoutPassed(machinePool *clusterv1.MachinePool) bool { if !machinePool.DeletionTimestamp.IsZero() && machinePool.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds != nil { if *machinePool.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds != 0 { deleteTimePlusDuration := machinePool.DeletionTimestamp.Add(time.Duration(*machinePool.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds) * time.Second) @@ -308,7 +303,7 @@ func (r *MachinePoolReconciler) isMachinePoolNodeDeleteTimeoutPassed(machinePool } // reconcileDeleteExternal tries to delete external references, returning true if it cannot find any. -func (r *MachinePoolReconciler) reconcileDeleteExternal(ctx context.Context, machinePool *clusterv1.MachinePool) (bool, error) { +func (r *Reconciler) reconcileDeleteExternal(ctx context.Context, machinePool *clusterv1.MachinePool) (bool, error) { objects := []*unstructured.Unstructured{} references := []clusterv1.ContractVersionedObjectReference{ machinePool.Spec.Template.Spec.Bootstrap.ConfigRef, @@ -344,7 +339,7 @@ func (r *MachinePoolReconciler) reconcileDeleteExternal(ctx context.Context, mac return len(objects) == 0, nil } -func (r *MachinePoolReconciler) watchClusterNodes(ctx context.Context, cluster *clusterv1.Cluster) error { +func (r *Reconciler) watchClusterNodes(ctx context.Context, cluster *clusterv1.Cluster) error { log := ctrl.LoggerFrom(ctx) if !conditions.IsTrue(cluster, clusterv1.ClusterControlPlaneInitializedCondition) { @@ -361,7 +356,7 @@ func (r *MachinePoolReconciler) watchClusterNodes(ctx context.Context, cluster * })) } -func (r *MachinePoolReconciler) nodeToMachinePool(ctx context.Context, o client.Object) []reconcile.Request { +func (r *Reconciler) nodeToMachinePool(ctx context.Context, o client.Object) []reconcile.Request { node, ok := o.(*corev1.Node) if !ok { panic(fmt.Sprintf("Expected a Node but got a %T", o)) @@ -415,7 +410,7 @@ func (r *MachinePoolReconciler) nodeToMachinePool(ctx context.Context, o client. return nil } -func (r *MachinePoolReconciler) getMachinesForMachinePool(ctx context.Context, s *scope) (ctrl.Result, error) { +func (r *Reconciler) getMachinesForMachinePool(ctx context.Context, s *scope) (ctrl.Result, error) { infraMachineSelector := metav1.LabelSelector{ MatchLabels: map[string]string{ clusterv1.MachinePoolNameLabel: format.MustFormatValue(s.machinePool.Name), @@ -445,7 +440,7 @@ func (r *MachinePoolReconciler) getMachinesForMachinePool(ctx context.Context, s return ctrl.Result{}, nil } -func (r *MachinePoolReconciler) setMachinesUptoDate(ctx context.Context, s *scope) (ctrl.Result, error) { +func (r *Reconciler) setMachinesUptoDate(ctx context.Context, s *scope) (ctrl.Result, error) { var errs []error for _, machine := range s.machines { patchHelper, err := patch.NewHelper(machine, r.Client) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller_noderef.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller_noderef.go similarity index 83% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller_noderef.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller_noderef.go index a5264c5327..cb35031839 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller_noderef.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller_noderef.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package machinepool import ( "context" @@ -47,7 +47,7 @@ type getNodeReferencesResult struct { ready int } -func (r *MachinePoolReconciler) reconcileNodeRefs(ctx context.Context, s *scope) (ctrl.Result, error) { +func (r *Reconciler) reconcileNodeRefs(ctx context.Context, s *scope) (ctrl.Result, error) { log := ctrl.LoggerFrom(ctx) cluster := s.cluster mp := s.machinePool @@ -70,8 +70,34 @@ func (r *MachinePoolReconciler) reconcileNodeRefs(ctx context.Context, s *scope) readyReplicas = mp.Status.Deprecated.V1Beta1.ReadyReplicas } if ptr.Deref(mp.Status.Replicas, 0) == readyReplicas && len(mp.Status.NodeRefs) == int(readyReplicas) { - v1beta1conditions.MarkTrue(mp, clusterv1.ReplicasReadyV1Beta1Condition) - return ctrl.Result{}, nil + // Validate that the UIDs in NodeRefs are still valid + if s.nodeRefMap != nil { + // Create a name-to-node mapping for efficient lookup + nodeNameMap := make(map[string]*corev1.Node, len(s.nodeRefMap)) + for _, node := range s.nodeRefMap { + nodeNameMap[node.Name] = node + } + + validNodeRefs := true + for _, nodeRef := range mp.Status.NodeRefs { + foundNode, exists := nodeNameMap[nodeRef.Name] + + // If node not found or UID doesn't match, mark as invalid + if !exists || foundNode.UID != nodeRef.UID { + log.V(1).Info("NodeRefs do not match current Nodes, will reassign") + validNodeRefs = false + break + } + } + + if validNodeRefs { + v1beta1conditions.MarkTrue(mp, clusterv1.ReplicasReadyV1Beta1Condition) + return ctrl.Result{}, nil + } + } else { + // If nodeRefMap is nil, we can't validate UIDs, so proceed with reconciliation + log.V(2).Info("NodeRefMap is nil, proceeding with reconciliation to validate NodeRefs") + } } // Check that the MachinePool has valid ProviderIDList. @@ -96,7 +122,7 @@ func (r *MachinePoolReconciler) reconcileNodeRefs(ctx context.Context, s *scope) nodeRefsResult, err := r.getNodeReferences(ctx, mp.Spec.ProviderIDList, ptr.Deref(mp.Spec.Template.Spec.MinReadySeconds, 0), s.nodeRefMap) if err != nil { - if err == errNoAvailableNodes { + if errors.Is(err, errNoAvailableNodes) { log.Info("Cannot assign NodeRefs to MachinePool, no matching Nodes") // No need to requeue here. Nodes emit an event that triggers reconciliation. return ctrl.Result{}, nil @@ -139,7 +165,7 @@ func (r *MachinePoolReconciler) reconcileNodeRefs(ctx context.Context, s *scope) // deleteRetiredNodes deletes nodes that don't have a corresponding ProviderID in Spec.ProviderIDList. // A MachinePool infrastructure provider indicates an instance in the set has been deleted by // removing its ProviderID from the slice. -func (r *MachinePoolReconciler) deleteRetiredNodes(ctx context.Context, c client.Client, nodeRefs []corev1.ObjectReference, providerIDList []string) error { +func (r *Reconciler) deleteRetiredNodes(ctx context.Context, c client.Client, nodeRefs []corev1.ObjectReference, providerIDList []string) error { log := ctrl.LoggerFrom(ctx, "providerIDList", len(providerIDList)) nodeRefsMap := make(map[string]*corev1.Node, len(nodeRefs)) for _, nodeRef := range nodeRefs { @@ -171,7 +197,7 @@ func (r *MachinePoolReconciler) deleteRetiredNodes(ctx context.Context, c client return nil } -func (r *MachinePoolReconciler) getNodeReferences(ctx context.Context, providerIDList []string, minReadySeconds int32, nodeRefsMap map[string]*corev1.Node) (getNodeReferencesResult, error) { +func (r *Reconciler) getNodeReferences(ctx context.Context, providerIDList []string, minReadySeconds int32, nodeRefsMap map[string]*corev1.Node) (getNodeReferencesResult, error) { log := ctrl.LoggerFrom(ctx, "providerIDList", len(providerIDList)) var ready, available int @@ -205,7 +231,7 @@ func (r *MachinePoolReconciler) getNodeReferences(ctx context.Context, providerI } // patchNodes patches the nodes with the cluster name and cluster namespace annotations. -func (r *MachinePoolReconciler) patchNodes(ctx context.Context, c client.Client, references []corev1.ObjectReference, mp *clusterv1.MachinePool) error { +func (r *Reconciler) patchNodes(ctx context.Context, c client.Client, references []corev1.ObjectReference, mp *clusterv1.MachinePool) error { log := ctrl.LoggerFrom(ctx) for _, nodeRef := range references { node := &corev1.Node{} @@ -220,7 +246,7 @@ func (r *MachinePoolReconciler) patchNodes(ctx context.Context, c client.Client, desired := map[string]string{ clusterv1.ClusterNameAnnotation: mp.Spec.ClusterName, clusterv1.ClusterNamespaceAnnotation: mp.GetNamespace(), - clusterv1.OwnerKindAnnotation: mp.Kind, + clusterv1.OwnerKindAnnotation: "MachinePool", clusterv1.OwnerNameAnnotation: mp.Name, } // Add annotations and drop NodeUninitializedTaint. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller_phases.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller_phases.go similarity index 94% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller_phases.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller_phases.go index 5af6a19275..07596b643b 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller_phases.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller_phases.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package machinepool import ( "context" @@ -39,7 +39,6 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/controllers/external" capierrors "sigs.k8s.io/cluster-api/errors" - utilexp "sigs.k8s.io/cluster-api/exp/util" "sigs.k8s.io/cluster-api/internal/contract" "sigs.k8s.io/cluster-api/internal/util/ssa" "sigs.k8s.io/cluster-api/util" @@ -51,7 +50,7 @@ import ( "sigs.k8s.io/cluster-api/util/predicates" ) -func (r *MachinePoolReconciler) reconcilePhase(mp *clusterv1.MachinePool) { +func (r *Reconciler) reconcilePhase(mp *clusterv1.MachinePool) { // Set the phase to "pending" if nil. if mp.Status.Phase == "" { mp.Status.SetTypedPhase(clusterv1.MachinePoolPhasePending) @@ -106,7 +105,7 @@ func (r *MachinePoolReconciler) reconcilePhase(mp *clusterv1.MachinePool) { } // reconcileExternal handles generic unstructured objects referenced by a MachinePool. -func (r *MachinePoolReconciler) reconcileExternal(ctx context.Context, m *clusterv1.MachinePool, ref clusterv1.ContractVersionedObjectReference) (external.ReconcileOutput, error) { +func (r *Reconciler) reconcileExternal(ctx context.Context, m *clusterv1.MachinePool, ref clusterv1.ContractVersionedObjectReference) (external.ReconcileOutput, error) { log := ctrl.LoggerFrom(ctx) obj, err := external.GetObjectFromContractVersionedRef(ctx, r.Client, ref, m.Namespace) @@ -179,7 +178,7 @@ func (r *MachinePoolReconciler) reconcileExternal(ctx context.Context, m *cluste } // reconcileBootstrap reconciles the Spec.Bootstrap.ConfigRef object on a MachinePool. -func (r *MachinePoolReconciler) reconcileBootstrap(ctx context.Context, s *scope) (ctrl.Result, error) { +func (r *Reconciler) reconcileBootstrap(ctx context.Context, s *scope) (ctrl.Result, error) { log := ctrl.LoggerFrom(ctx) m := s.machinePool // Call generic external reconciler if we have an external reference. @@ -249,7 +248,7 @@ func (r *MachinePoolReconciler) reconcileBootstrap(ctx context.Context, s *scope } // reconcileInfrastructure reconciles the Spec.InfrastructureRef object on a MachinePool. -func (r *MachinePoolReconciler) reconcileInfrastructure(ctx context.Context, s *scope) (ctrl.Result, error) { +func (r *Reconciler) reconcileInfrastructure(ctx context.Context, s *scope) (ctrl.Result, error) { log := ctrl.LoggerFrom(ctx) cluster := s.cluster mp := s.machinePool @@ -324,7 +323,7 @@ func (r *MachinePoolReconciler) reconcileInfrastructure(ctx context.Context, s * // Get and set Status.Replicas from the infrastructure provider. err = util.UnstructuredUnmarshalField(infraConfig, &mp.Status.Replicas, "status", "replicas") if err != nil { - if err != util.ErrUnstructuredFieldNotFound { + if !errors.Is(err, util.ErrUnstructuredFieldNotFound) { return ctrl.Result{}, errors.Wrapf(err, "failed to retrieve replicas from infrastructure provider for MachinePool %q in namespace %q", mp.Name, mp.Namespace) } } @@ -357,7 +356,7 @@ func (r *MachinePoolReconciler) reconcileInfrastructure(ctx context.Context, s * // infrastructure is created accordingly. // Note: When supported by the cloud provider implementation of the MachinePool, machines will provide a means to interact // with the corresponding infrastructure (e.g. delete a specific machine in case MachineHealthCheck detects it is unhealthy). -func (r *MachinePoolReconciler) reconcileMachines(ctx context.Context, s *scope, infraMachinePool *unstructured.Unstructured) error { +func (r *Reconciler) reconcileMachines(ctx context.Context, s *scope, infraMachinePool *unstructured.Unstructured) error { log := ctrl.LoggerFrom(ctx) mp := s.machinePool @@ -414,7 +413,7 @@ func (r *MachinePoolReconciler) reconcileMachines(ctx context.Context, s *scope, } // createOrUpdateMachines creates a MachinePool Machine for each infraMachine if it doesn't already exist and sets the owner reference and infraRef. -func (r *MachinePoolReconciler) createOrUpdateMachines(ctx context.Context, s *scope, machines []clusterv1.Machine, infraMachines []unstructured.Unstructured) error { +func (r *Reconciler) createOrUpdateMachines(ctx context.Context, s *scope, machines []clusterv1.Machine, infraMachines []unstructured.Unstructured) error { log := ctrl.LoggerFrom(ctx) // Construct a set of names of infraMachines that already have a Machine. @@ -473,7 +472,7 @@ func (r *MachinePoolReconciler) createOrUpdateMachines(ctx context.Context, s *s // computeDesiredMachine constructs the desired Machine for an infraMachine. // If the Machine exists, it ensures the Machine always owned by the MachinePool. -func (r *MachinePoolReconciler) computeDesiredMachine(mp *clusterv1.MachinePool, infraMachine *unstructured.Unstructured, existingMachine *clusterv1.Machine, existingNode *corev1.Node) *clusterv1.Machine { +func (r *Reconciler) computeDesiredMachine(mp *clusterv1.MachinePool, infraMachine *unstructured.Unstructured, existingMachine *clusterv1.Machine, existingNode *corev1.Node) *clusterv1.Machine { infraRef := clusterv1.ContractVersionedObjectReference{ APIGroup: infraMachine.GroupVersionKind().Group, Kind: infraMachine.GetKind(), @@ -531,11 +530,11 @@ func (r *MachinePoolReconciler) computeDesiredMachine(mp *clusterv1.MachinePool, // infraMachineToMachinePoolMapper is a mapper function that maps an InfraMachine to the MachinePool that owns it. // This is used to trigger an update of the MachinePool when a InfraMachine is changed. -func (r *MachinePoolReconciler) infraMachineToMachinePoolMapper(ctx context.Context, o client.Object) []ctrl.Request { +func (r *Reconciler) infraMachineToMachinePoolMapper(ctx context.Context, o client.Object) []ctrl.Request { log := ctrl.LoggerFrom(ctx) if labels.IsMachinePoolOwned(o) { - machinePool, err := utilexp.GetMachinePoolByLabels(ctx, r.Client, o.GetNamespace(), o.GetLabels()) + machinePool, err := util.GetMachinePoolByLabels(ctx, r.Client, o.GetNamespace(), o.GetLabels()) if err != nil { log.Error(err, "Failed to get MachinePool for InfraMachine", o.GetObjectKind().GroupVersionKind().Kind, klog.KObj(o), "labels", o.GetLabels()) return nil @@ -555,7 +554,7 @@ func (r *MachinePoolReconciler) infraMachineToMachinePoolMapper(ctx context.Cont return nil } -func (r *MachinePoolReconciler) waitForMachineCreation(ctx context.Context, machineList []clusterv1.Machine) error { +func (r *Reconciler) waitForMachineCreation(ctx context.Context, machineList []clusterv1.Machine) error { _ = ctrl.LoggerFrom(ctx) // waitForCacheUpdateTimeout is the amount of time allowed to wait for desired state. @@ -587,7 +586,7 @@ func (r *MachinePoolReconciler) waitForMachineCreation(ctx context.Context, mach return nil } -func (r *MachinePoolReconciler) getNodeRefMap(ctx context.Context, c client.Client) (map[string]*corev1.Node, error) { +func (r *Reconciler) getNodeRefMap(ctx context.Context, c client.Client) (map[string]*corev1.Node, error) { log := ctrl.LoggerFrom(ctx) nodeRefsMap := make(map[string]*corev1.Node) nodeList := corev1.NodeList{} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller_scope.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller_scope.go similarity index 99% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller_scope.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller_scope.go index 1bb60bb475..026bb393ba 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller_scope.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller_scope.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package machinepool import ( "errors" diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller_status.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller_status.go similarity index 95% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller_status.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller_status.go index 6f5df81ad9..24d23c1527 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/controllers/machinepool_controller_status.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machinepool/machinepool_controller_status.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package machinepool import ( "context" @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/cluster-api/util/conditions" ) -func (r *MachinePoolReconciler) updateStatus(ctx context.Context, s *scope) error { +func (r *Reconciler) updateStatus(ctx context.Context, s *scope) error { log := ctrl.LoggerFrom(ctx) if s.infraMachinePool == nil { diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_controller.go index c39bf6537a..7b4517c0d8 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_controller.go @@ -34,25 +34,27 @@ import ( kerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/record" "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" "sigs.k8s.io/cluster-api/controllers/clustercache" "sigs.k8s.io/cluster-api/controllers/external" "sigs.k8s.io/cluster-api/controllers/noderefutil" "sigs.k8s.io/cluster-api/internal/contract" "sigs.k8s.io/cluster-api/internal/controllers/machine" - "sigs.k8s.io/cluster-api/internal/controllers/machinedeployment/mdutil" + "sigs.k8s.io/cluster-api/internal/hooks" topologynames "sigs.k8s.io/cluster-api/internal/topology/names" + clientutil "sigs.k8s.io/cluster-api/internal/util/client" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" + "sigs.k8s.io/cluster-api/internal/util/inplace" "sigs.k8s.io/cluster-api/internal/util/ssa" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" @@ -70,16 +72,12 @@ import ( var ( // machineSetKind contains the schema.GroupVersionKind for the MachineSet type. machineSetKind = clusterv1.GroupVersion.WithKind("MachineSet") - - // stateConfirmationTimeout is the amount of time allowed to wait for desired state. - stateConfirmationTimeout = 10 * time.Second - - // stateConfirmationInterval is the amount of time between polling for the desired state. - // The polling is against a local memory cache. - stateConfirmationInterval = 100 * time.Millisecond ) -const machineSetManagerName = "capi-machineset" +const ( + machineSetManagerName = "capi-machineset" + machineSetMetadataManagerName = "capi-machineset-metadata" +) // Update permissions on /finalizers subresrouce is required on management clusters with 'OwnerReferencesPermissionEnforcement' plugin enabled. // See: https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement @@ -102,6 +100,15 @@ type Reconciler struct { ssaCache ssa.Cache recorder record.EventRecorder + + // Note: This field is only used for unit tests that use fake client because the fake client does not properly set resourceVersion + // on BootstrapConfig/InfraMachine after ssa.Patch and then ssa.RemoveManagedFieldsForLabelsAndAnnotations would fail. + disableRemoveManagedFieldsForLabelsAndAnnotations bool + + // Those fields are only used for test purposes. + overrideCreateMachines func(ctx context.Context, s *scope, machinesToAdd int) (ctrl.Result, error) + overrideMoveMachines func(ctx context.Context, s *scope, targetMSName string, machinesToMove int) (ctrl.Result, error) + overrideDeleteMachines func(ctx context.Context, s *scope, machinesToDelete int) (ctrl.Result, error) } func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { @@ -119,33 +126,26 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt return err } - err = ctrl.NewControllerManagedBy(mgr). + err = capicontrollerutil.NewControllerManagedBy(mgr, predicateLog). For(&clusterv1.MachineSet{}). - Owns(&clusterv1.Machine{}, builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog))). + Owns(&clusterv1.Machine{}). // Watches enqueues MachineSet for corresponding Machine resources, if no managed controller reference (owner) exists. Watches( &clusterv1.Machine{}, handler.EnqueueRequestsFromMapFunc(r.MachineToMachineSets), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog)), ). Watches( &clusterv1.MachineDeployment{}, handler.EnqueueRequestsFromMapFunc(mdToMachineSets), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog)), ). WithOptions(options). WithEventFilter(predicates.ResourceHasFilterLabel(mgr.GetScheme(), predicateLog, r.WatchFilterValue)). Watches( &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(clusterToMachineSets), - builder.WithPredicates( - // TODO: should this wait for Cluster.Status.InfrastructureReady similar to Infra Machine resources? - predicates.All(mgr.GetScheme(), predicateLog, - predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog), - predicates.ClusterPausedTransitions(mgr.GetScheme(), predicateLog), - predicates.ResourceHasFilterLabel(mgr.GetScheme(), predicateLog, r.WatchFilterValue), - ), - ), + // TODO: should this wait for Cluster.Status.InfrastructureReady similar to Infra Machine resources? + predicates.ClusterPausedTransitions(mgr.GetScheme(), predicateLog), + predicates.ResourceHasFilterLabel(mgr.GetScheme(), predicateLog, r.WatchFilterValue), ). WatchesRawSource(r.ClusterCache.GetClusterSource("machineset", clusterToMachineSets)). Complete(r) @@ -223,11 +223,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (retres ct reterr = kerrors.NewAggregate([]error{reterr, err}) } - if reterr != nil { - retres = ctrl.Result{} - return - } - // Adjust requeue when scaling up if s.machineSet.DeletionTimestamp.IsZero() && reterr == nil { retres = util.LowestNonZeroResult(retres, shouldRequeueForReplicaCountersRefresh(s)) @@ -242,11 +237,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (retres ct } } - alwaysReconcile := []machineSetReconcileFunc{ + alwaysReconcile := []machineSetReconcileBlockingFunc{ wrapErrMachineSetReconcileFunc(r.reconcileMachineSetOwnerAndLabels, "failed to set MachineSet owner and labels"), wrapErrMachineSetReconcileFunc(r.reconcileInfrastructure, "failed to reconcile infrastructure"), wrapErrMachineSetReconcileFunc(r.reconcileBootstrapConfig, "failed to reconcile bootstrapConfig"), - wrapErrMachineSetReconcileFunc(r.getAndAdoptMachinesForMachineSet, "failed to get and adopt Machines for MachineSet"), + wrapErrMachineSetReconcileBlockingFunc(r.getAndAdoptMachinesForMachineSet, "failed to get and adopt Machines for MachineSet"), } // Handle deletion reconciliation loop. @@ -260,7 +255,8 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (retres ct reconcileNormal := append(alwaysReconcile, wrapErrMachineSetReconcileFunc(r.reconcileUnhealthyMachines, "failed to reconcile unhealthy machines"), - wrapErrMachineSetReconcileFunc(r.syncMachines, "failed to sync Machines"), + wrapErrMachineSetReconcileBlockingFunc(r.syncMachines, "failed to sync Machines"), + wrapErrMachineSetReconcileFunc(r.triggerInPlaceUpdate, "failed to trigger in-place update"), wrapErrMachineSetReconcileFunc(r.syncReplicas, "failed to sync replicas"), ) @@ -279,21 +275,30 @@ type scope struct { reconciliationTime time.Time } +type machineSetReconcileBlockingFunc func(ctx context.Context, s *scope) (ctrl.Result, bool, error) + type machineSetReconcileFunc func(ctx context.Context, s *scope) (ctrl.Result, error) -func wrapErrMachineSetReconcileFunc(f machineSetReconcileFunc, msg string) machineSetReconcileFunc { - return func(ctx context.Context, s *scope) (ctrl.Result, error) { +func wrapErrMachineSetReconcileBlockingFunc(f machineSetReconcileBlockingFunc, msg string) machineSetReconcileBlockingFunc { + return func(ctx context.Context, s *scope) (ctrl.Result, bool, error) { + res, stopReconcile, err := f(ctx, s) + return res, stopReconcile, errors.Wrap(err, msg) + } +} + +func wrapErrMachineSetReconcileFunc(f machineSetReconcileFunc, msg string) machineSetReconcileBlockingFunc { + return func(ctx context.Context, s *scope) (ctrl.Result, bool, error) { res, err := f(ctx, s) - return res, errors.Wrap(err, msg) + return res, false, errors.Wrap(err, msg) } } -func doReconcile(ctx context.Context, s *scope, phases []machineSetReconcileFunc) (ctrl.Result, kerrors.Aggregate) { +func doReconcile(ctx context.Context, s *scope, phases []machineSetReconcileBlockingFunc) (ctrl.Result, kerrors.Aggregate) { res := ctrl.Result{} errs := []error{} for _, phase := range phases { // Call the inner reconciliation methods. - phaseResult, err := phase(ctx, s) + phaseResult, stopReconcile, err := phase(ctx, s) if err != nil { errs = append(errs, err) } @@ -301,6 +306,9 @@ func doReconcile(ctx context.Context, s *scope, phases []machineSetReconcileFunc continue } res = util.LowestNonZeroResult(res, phaseResult) + if stopReconcile { + break + } } if len(errs) > 0 { @@ -341,6 +349,159 @@ func patchMachineSet(ctx context.Context, patchHelper *patch.Helper, machineSet return patchHelper.Patch(ctx, machineSet, options...) } +// triggerInPlaceUpdate func completes the move started when scaling down and then trigger the in-place update. +// Note: Usually it is the new MS that receives Machines after a move operation. +func (r *Reconciler) triggerInPlaceUpdate(ctx context.Context, s *scope) (ctrl.Result, error) { + log := ctrl.LoggerFrom(ctx) + + errs := []error{} + machinesTriggeredInPlace := []*clusterv1.Machine{} + for _, machine := range s.machines { + log := log.WithValues("Machine", klog.KObj(machine)) + + // If a machine is not updating in place, or if the in-place update has been already triggered, no-op + if _, ok := machine.Annotations[clusterv1.UpdateInProgressAnnotation]; !ok || hooks.IsPending(runtimehooksv1.UpdateMachine, machine) { + continue + } + + // If the existing machine is pending acknowledge from the MD controller after a move operation, + // wait until if it possible to drop the PendingAcknowledgeMove annotation. + orig := machine.DeepCopy() + if _, ok := machine.Annotations[clusterv1.PendingAcknowledgeMoveAnnotation]; ok { + // Check if this MachineSet is still accepting machines moved from other MachineSets. + if sourceMSs, ok := s.machineSet.Annotations[clusterv1.MachineSetReceiveMachinesFromMachineSetsAnnotation]; ok && sourceMSs != "" { + // Get the list of machines acknowledged by the MD controller. + acknowledgedMoveReplicas := sets.Set[string]{} + if replicaNames, ok := s.machineSet.Annotations[clusterv1.AcknowledgedMoveAnnotation]; ok && replicaNames != "" { + acknowledgedMoveReplicas.Insert(strings.Split(replicaNames, ",")...) + } + + // If the current machine is in not yet in the list, it is not possible to trigger in-place yet. + if !acknowledgedMoveReplicas.Has(machine.Name) { + continue + } + + // If the current machine is in the list, drop the annotation. + delete(machine.Annotations, clusterv1.PendingAcknowledgeMoveAnnotation) + } else { + // If this MachineSet is not accepting anymore machines from other MS (e.g. because of MD spec changes), + // then drop the PendingAcknowledgeMove annotation; this machine will be treated as any other machine and either + // deleted or moved to another MS after completing the in-place update. + delete(machine.Annotations, clusterv1.PendingAcknowledgeMoveAnnotation) + } + } + + // Complete the move operation started by the source MachinesSet by updating machine, infraMachine and boostrapConfig + // to align to the desiredState for the current MachineSet. + if err := r.completeMoveMachine(ctx, s, machine); err != nil { + errs = append(errs, err) + continue + } + + // Note: Once we write PendingHooksAnnotation the Machine controller will start with the in-place update. + hooks.MarkObjectAsPending(machine, runtimehooksv1.UpdateMachine) + + // Note: Intentionally using client.Patch instead of SSA. Otherwise we would + // have to ensure we preserve PendingHooksAnnotation on existing Machines in MachineSet and that would lead to race + // conditions when the Machine controller tries to remove the annotation and MachineSet adds it back. + if err := r.Client.Patch(ctx, machine, client.MergeFrom(orig)); err != nil { + errs = append(errs, errors.Wrapf(err, "failed to complete triggering in-place update for Machine %s", klog.KObj(machine))) + continue + } + + machinesTriggeredInPlace = append(machinesTriggeredInPlace, machine) + log.Info(fmt.Sprintf("Completed triggering in-place update for Machine %s", machine.Name)) + r.recorder.Event(machine, corev1.EventTypeNormal, "SuccessfulStartInPlaceUpdate", "Machine starting in-place update") + } + + // Wait until the cache observed the Machine with PendingHooksAnnotation to ensure subsequent reconciles + // will observe it as well and won't repeatedly call the logic to trigger in-place update. + if err := clientutil.WaitForCacheToBeUpToDate(ctx, r.Client, "complete triggering in-place update", machinesTriggeredInPlace...); err != nil { + errs = append(errs, err) + } + if len(errs) > 0 { + return ctrl.Result{}, kerrors.NewAggregate(errs) + } + return ctrl.Result{}, nil +} + +func (r *Reconciler) completeMoveMachine(ctx context.Context, s *scope, currentMachine *clusterv1.Machine) error { + desiredMachine, err := r.computeDesiredMachine(s.machineSet, currentMachine) + if err != nil { + return errors.Wrap(err, "could not compute desired Machine") + } + // Note: spec.version and spec.failureDomain are not mutated in-place by syncMachines and accordingly + // not updated by r.computeDesiredMachine, so we have to update them here. + // Note: for MachineSets we have to explicitly also set spec.failureDomain (this is a difference from what happens in KCP + // where the field is set only on create and never updated) + desiredMachine.Spec.Version = s.machineSet.Spec.Template.Spec.Version + desiredMachine.Spec.FailureDomain = s.machineSet.Spec.Template.Spec.FailureDomain + + // Compute desiredInfraMachine. + currentInfraMachine, err := external.GetObjectFromContractVersionedRef(ctx, r.Client, currentMachine.Spec.InfrastructureRef, currentMachine.Namespace) + if err != nil { + return errors.Wrapf(err, "failed to get InfraMachine %s", klog.KRef(currentMachine.Namespace, currentMachine.Spec.InfrastructureRef.Name)) + } + desiredInfraMachine, err := r.computeDesiredInfraMachine(ctx, s.machineSet, currentMachine, currentInfraMachine) + if err != nil { + return errors.Wrap(err, "could not compute desired InfraMachine") + } + + // Make sure we drop the fields that should be continuously updated by syncMachines using the capi-machineset-metadata field owner + // from the desiredInfraMachine (drop all labels and annotations except clonedFrom). + desiredInfraMachine.SetLabels(nil) + desiredInfraMachine.SetAnnotations(map[string]string{ + clusterv1.TemplateClonedFromNameAnnotation: desiredInfraMachine.GetAnnotations()[clusterv1.TemplateClonedFromNameAnnotation], + clusterv1.TemplateClonedFromGroupKindAnnotation: desiredInfraMachine.GetAnnotations()[clusterv1.TemplateClonedFromGroupKindAnnotation], + // Machine controller waits for this annotation to exist on Machine and related objects before starting the in-place update. + clusterv1.UpdateInProgressAnnotation: "", + }) + + var desiredBootstrapConfig, currentBootstrapConfig *unstructured.Unstructured + if currentMachine.Spec.Bootstrap.ConfigRef.IsDefined() { + // Compute desiredBootstrapConfig. + currentBootstrapConfig, err = external.GetObjectFromContractVersionedRef(ctx, r.Client, currentMachine.Spec.Bootstrap.ConfigRef, currentMachine.Namespace) + if err != nil { + return errors.Wrapf(err, "failed to get BootstrapConfig %s", klog.KRef(currentMachine.Namespace, currentMachine.Spec.Bootstrap.ConfigRef.Name)) + } + + desiredBootstrapConfig, err = r.computeDesiredBootstrapConfig(ctx, s.machineSet, currentMachine, currentBootstrapConfig) + if err != nil { + return errors.Wrap(err, "could not compute desired BootstrapConfig") + } + + // Make sure we drop the fields that should be continuously updated by syncMachines using the capi-machineset-metadata field owner + // from the desiredBootstrapConfig (drop all labels and annotations except clonedFrom). + desiredBootstrapConfig.SetLabels(nil) + desiredBootstrapConfig.SetAnnotations(map[string]string{ + clusterv1.TemplateClonedFromNameAnnotation: desiredBootstrapConfig.GetAnnotations()[clusterv1.TemplateClonedFromNameAnnotation], + clusterv1.TemplateClonedFromGroupKindAnnotation: desiredBootstrapConfig.GetAnnotations()[clusterv1.TemplateClonedFromGroupKindAnnotation], + // Machine controller waits for this annotation to exist on Machine and related objects before starting the in-place update. + clusterv1.UpdateInProgressAnnotation: "", + }) + } + + // Write InfraMachine. + // Note: Let's update InfraMachine first because that is the call that is most likely to fail. + if err := ssa.Patch(ctx, r.Client, machineSetManagerName, desiredInfraMachine); err != nil { + return errors.Wrapf(err, "failed to complete triggering in-place update for Machine %s", klog.KObj(desiredMachine)) + } + + // Write BootstrapConfig. + if desiredMachine.Spec.Bootstrap.ConfigRef.IsDefined() { + if err := ssa.Patch(ctx, r.Client, machineSetManagerName, desiredBootstrapConfig); err != nil { + return errors.Wrapf(err, "failed to complete triggering in-place update for Machine %s", klog.KObj(desiredMachine)) + } + } + + // Write Machine. + if err := ssa.Patch(ctx, r.Client, machineSetManagerName, desiredMachine); err != nil { + return errors.Wrapf(err, "failed to complete triggering in-place update for Machine %s", klog.KObj(desiredMachine)) + } + + return nil +} + func (r *Reconciler) reconcileMachineSetOwnerAndLabels(_ context.Context, s *scope) (ctrl.Result, error) { machineSet := s.machineSet cluster := s.cluster @@ -405,9 +566,6 @@ func (r *Reconciler) reconcileBootstrapConfig(ctx context.Context, s *scope) (ct func (r *Reconciler) reconcileDelete(ctx context.Context, s *scope) (ctrl.Result, error) { machineSet := s.machineSet machineList := s.machines - if !s.getAndAdoptMachinesForMachineSetSucceeded { - return ctrl.Result{}, nil - } log := ctrl.LoggerFrom(ctx) @@ -425,7 +583,7 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, s *scope) (ctrl.Result } // Note: We intentionally log after Delete because we want this log line to show up only after DeletionTimestamp has been set. // Also, setting DeletionTimestamp doesn't mean the Machine is actually deleted (deletion takes some time). - log.Info("Deleting Machine (MachineSet deleted)", "Machine", klog.KObj(machine)) + log.Info(fmt.Sprintf("Deleting Machine %s (MachineSet deleted)", machine.Name), "Machine", klog.KObj(machine)) } } @@ -433,12 +591,12 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, s *scope) (ctrl.Result return ctrl.Result{}, nil } -func (r *Reconciler) getAndAdoptMachinesForMachineSet(ctx context.Context, s *scope) (ctrl.Result, error) { +func (r *Reconciler) getAndAdoptMachinesForMachineSet(ctx context.Context, s *scope) (ctrl.Result, bool, error) { machineSet := s.machineSet log := ctrl.LoggerFrom(ctx) selectorMap, err := metav1.LabelSelectorAsMap(&machineSet.Spec.Selector) if err != nil { - return ctrl.Result{}, errors.Wrapf(err, "failed to convert MachineSet %q label selector to a map", machineSet.Name) + return ctrl.Result{}, true, errors.Wrapf(err, "failed to convert MachineSet %q label selector to a map", machineSet.Name) } // Get all Machines linked to this MachineSet. @@ -449,7 +607,7 @@ func (r *Reconciler) getAndAdoptMachinesForMachineSet(ctx context.Context, s *sc client.MatchingLabels(selectorMap), ) if err != nil { - return ctrl.Result{}, errors.Wrap(err, "failed to list machines") + return ctrl.Result{}, true, errors.Wrap(err, "failed to list machines") } // Filter out irrelevant machines (i.e. IsControlledBy something else) and claim orphaned machines. @@ -480,36 +638,27 @@ func (r *Reconciler) getAndAdoptMachinesForMachineSet(ctx context.Context, s *sc s.machines = filteredMachines s.getAndAdoptMachinesForMachineSetSucceeded = true - return ctrl.Result{}, nil + return ctrl.Result{}, false, nil } // syncMachines updates Machines, InfrastructureMachine and BootstrapConfig to propagate in-place mutable fields // from the MachineSet. -// Note: It also cleans up managed fields of all Machines so that Machines that were -// created/patched before (< v1.4.0) the controller adopted Server-Side-Apply (SSA) can also work with SSA. -// Note: For InfrastructureMachines and BootstrapConfigs it also drops ownership of "metadata.labels" and -// "metadata.annotations" from "manager" so that "capi-machineset" can own these fields and can work with SSA. -// Otherwise fields would be co-owned by our "old" "manager" and "capi-machineset" and then we would not be -// able to e.g. drop labels and annotations. -func (r *Reconciler) syncMachines(ctx context.Context, s *scope) (ctrl.Result, error) { +func (r *Reconciler) syncMachines(ctx context.Context, s *scope) (ctrl.Result, bool, error) { machineSet := s.machineSet machines := s.machines - if !s.getAndAdoptMachinesForMachineSetSucceeded { - return ctrl.Result{}, nil - } log := ctrl.LoggerFrom(ctx) + + var anyManagedFieldIssueMitigated bool for i := range machines { m := machines[i] - upToDateCondition := newMachineUpToDateCondition(s) - // If the machine is already being deleted, we only need to sync // the subset of fields that impact tearing down a machine if !m.DeletionTimestamp.IsZero() { patchHelper, err := patch.NewHelper(m, r.Client) if err != nil { - return ctrl.Result{}, err + return ctrl.Result{}, true, err } // Set all other in-place mutable fields that impact the ability to tear down existing machines. @@ -518,347 +667,426 @@ func (r *Reconciler) syncMachines(ctx context.Context, s *scope) (ctrl.Result, e m.Spec.Deletion.NodeDeletionTimeoutSeconds = machineSet.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds m.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = machineSet.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds m.Spec.MinReadySeconds = machineSet.Spec.Template.Spec.MinReadySeconds + m.Spec.Taints = machineSet.Spec.Template.Spec.Taints - // Set machine's up to date condition - if upToDateCondition != nil { - conditions.Set(m, *upToDateCondition) - } - - if err := patchHelper.Patch(ctx, m, patch.WithOwnedConditions{Conditions: []string{clusterv1.MachineUpToDateCondition}}); err != nil { - return ctrl.Result{}, err + if err := patchHelper.Patch(ctx, m); err != nil { + return ctrl.Result{}, true, err } continue } - // Patch the machine's up-to-date condition. - // Note: for the time being we continue to rely on the patch helper for setting conditions; In the future, if - // we will improve patch helper to support SSA, we can revisit this code and perform both this change and the others in place mutations in a single operation. - if upToDateCondition != nil { - patchHelper, err := patch.NewHelper(m, r.Client) + managedFieldIssueMitigated, err := ssa.MitigateManagedFieldsIssue(ctx, r.Client, m, machineSetManagerName) + if err != nil { + return ctrl.Result{}, true, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + if !anyManagedFieldIssueMitigated { + // Update Machine to propagate in-place mutable fields from the MachineSet. + updatedMachine, err := r.computeDesiredMachine(machineSet, m) if err != nil { - return ctrl.Result{}, err + return ctrl.Result{}, true, errors.Wrap(err, "failed to update Machine: failed to compute desired Machine") } - conditions.Set(m, *upToDateCondition) - if err := patchHelper.Patch(ctx, m, patch.WithOwnedConditions{Conditions: []string{clusterv1.MachineUpToDateCondition}}); err != nil { - return ctrl.Result{}, err + err = ssa.Patch(ctx, r.Client, machineSetManagerName, updatedMachine, ssa.WithCachingProxy{Cache: r.ssaCache, Original: m}) + if err != nil { + log.Error(err, "Failed to update Machine", "Machine", klog.KObj(updatedMachine)) + return ctrl.Result{}, true, errors.Wrapf(err, "failed to update Machine %q", klog.KObj(updatedMachine)) } + machines[i] = updatedMachine } - // Cleanup managed fields of all Machines. - // We do this so that Machines that were created/patched before the controller adopted Server-Side-Apply (SSA) - // (< v1.4.0) can also work with SSA. Otherwise, fields would be co-owned by our "old" "manager" and - // "capi-machineset" and then we would not be able to e.g. drop labels and annotations. - if err := ssa.CleanUpManagedFieldsForSSAAdoption(ctx, r.Client, m, machineSetManagerName); err != nil { - return ctrl.Result{}, errors.Wrapf(err, "failed to update machine: failed to adjust the managedFields of the Machine %q", m.Name) - } - - // Update Machine to propagate in-place mutable fields from the MachineSet. - updatedMachine, err := r.computeDesiredMachine(machineSet, m) - if err != nil { - return ctrl.Result{}, errors.Wrap(err, "failed to update Machine: failed to compute desired Machine") - } - err = ssa.Patch(ctx, r.Client, machineSetManagerName, updatedMachine, ssa.WithCachingProxy{Cache: r.ssaCache, Original: m}) + infraMachine, err := external.GetObjectFromContractVersionedRef(ctx, r.Client, m.Spec.InfrastructureRef, m.Namespace) if err != nil { - log.Error(err, "Failed to update Machine", "Machine", klog.KObj(updatedMachine)) - return ctrl.Result{}, errors.Wrapf(err, "failed to update Machine %q", klog.KObj(updatedMachine)) + return ctrl.Result{}, true, errors.Wrapf(err, "failed to get InfrastructureMachine %s %s", + m.Spec.InfrastructureRef.Kind, klog.KRef(m.Namespace, m.Spec.InfrastructureRef.Name)) } - machines[i] = updatedMachine - - infraMachine, err := external.GetObjectFromContractVersionedRef(ctx, r.Client, updatedMachine.Spec.InfrastructureRef, updatedMachine.Namespace) + managedFieldIssueMitigated, err = ssa.MitigateManagedFieldsIssue(ctx, r.Client, infraMachine, machineSetMetadataManagerName) if err != nil { - return ctrl.Result{}, errors.Wrapf(err, "failed to get InfrastructureMachine %s %s", - updatedMachine.Spec.InfrastructureRef.Kind, klog.KRef(updatedMachine.Namespace, updatedMachine.Spec.InfrastructureRef.Name)) - } - // Cleanup managed fields of all InfrastructureMachines to drop ownership of labels and annotations - // from "manager". We do this so that InfrastructureMachines that are created using the Create method - // can also work with SSA. Otherwise, labels and annotations would be co-owned by our "old" "manager" - // and "capi-machineset" and then we would not be able to e.g. drop labels and annotations. - labelsAndAnnotationsManagedFieldPaths := []contract.Path{ - {"f:metadata", "f:annotations"}, - {"f:metadata", "f:labels"}, - } - if err := ssa.DropManagedFields(ctx, r.Client, infraMachine, machineSetManagerName, labelsAndAnnotationsManagedFieldPaths); err != nil { - return ctrl.Result{}, errors.Wrapf(err, "failed to update machine: failed to adjust the managedFields of the InfrastructureMachine %s", klog.KObj(infraMachine)) - } - // Update in-place mutating fields on InfrastructureMachine. - if err := r.updateExternalObject(ctx, infraMachine, machineSet); err != nil { - return ctrl.Result{}, errors.Wrapf(err, "failed to update InfrastructureMachine %s", klog.KObj(infraMachine)) + return ctrl.Result{}, true, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + if !anyManagedFieldIssueMitigated { + // Drop managedFields for manager:Update and capi-machineset:Apply for all objects created with CAPI <= v1.11. + // Starting with CAPI v1.12 we have a new managedField structure where capi-machineset-metadata will own + // labels and annotations and capi-machineset everything else. + // Note: We have to call ssa.MigrateManagedFields for every Machine created with CAPI <= v1.11 once. + // Given that this was introduced in CAPI v1.12 and our n-3 upgrade policy this can + // be removed with CAPI v1.15. + if err := ssa.MigrateManagedFields(ctx, r.Client, infraMachine, machineSetManagerName, machineSetMetadataManagerName); err != nil { + return ctrl.Result{}, true, errors.Wrapf(err, "failed to clean up managedFields of InfrastructureMachine %s", klog.KObj(infraMachine)) + } + // Update in-place mutating fields on InfrastructureMachine. + if err := r.updateLabelsAndAnnotations(ctx, infraMachine, machineSet); err != nil { + return ctrl.Result{}, true, errors.Wrapf(err, "failed to update InfrastructureMachine %s", klog.KObj(infraMachine)) + } } - if updatedMachine.Spec.Bootstrap.ConfigRef.IsDefined() { - bootstrapConfig, err := external.GetObjectFromContractVersionedRef(ctx, r.Client, updatedMachine.Spec.Bootstrap.ConfigRef, updatedMachine.Namespace) + if m.Spec.Bootstrap.ConfigRef.IsDefined() { + bootstrapConfig, err := external.GetObjectFromContractVersionedRef(ctx, r.Client, m.Spec.Bootstrap.ConfigRef, m.Namespace) if err != nil { - return ctrl.Result{}, errors.Wrapf(err, "failed to get BootstrapConfig %s %s", - updatedMachine.Spec.Bootstrap.ConfigRef.Kind, klog.KRef(updatedMachine.Namespace, updatedMachine.Spec.Bootstrap.ConfigRef.Name)) + return ctrl.Result{}, true, errors.Wrapf(err, "failed to get BootstrapConfig %s %s", + m.Spec.Bootstrap.ConfigRef.Kind, klog.KRef(m.Namespace, m.Spec.Bootstrap.ConfigRef.Name)) } - // Cleanup managed fields of all BootstrapConfigs to drop ownership of labels and annotations - // from "manager". We do this so that BootstrapConfigs that are created using the Create method - // can also work with SSA. Otherwise, labels and annotations would be co-owned by our "old" "manager" - // and "capi-machineset" and then we would not be able to e.g. drop labels and annotations. - if err := ssa.DropManagedFields(ctx, r.Client, bootstrapConfig, machineSetManagerName, labelsAndAnnotationsManagedFieldPaths); err != nil { - return ctrl.Result{}, errors.Wrapf(err, "failed to update machine: failed to adjust the managedFields of the BootstrapConfig %s", klog.KObj(bootstrapConfig)) + managedFieldIssueMitigated, err = ssa.MitigateManagedFieldsIssue(ctx, r.Client, bootstrapConfig, machineSetMetadataManagerName) + if err != nil { + return ctrl.Result{}, true, err } - // Update in-place mutating fields on BootstrapConfig. - if err := r.updateExternalObject(ctx, bootstrapConfig, machineSet); err != nil { - return ctrl.Result{}, errors.Wrapf(err, "failed to update BootstrapConfig %s", klog.KObj(bootstrapConfig)) + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + if !anyManagedFieldIssueMitigated { + // Drop managedFields for manager:Update and capi-machineset:Apply for all objects created with CAPI <= v1.11. + // Starting with CAPI v1.12 we have a new managedField structure where capi-machineset-metadata will own + // labels and annotations and capi-machineset everything else. + // Note: We have to call ssa.MigrateManagedFields for every Machine created with CAPI <= v1.11 once. + // Given that this was introduced in CAPI v1.12 and our n-3 upgrade policy this can + // be removed with CAPI v1.15. + if err := ssa.MigrateManagedFields(ctx, r.Client, bootstrapConfig, machineSetManagerName, machineSetMetadataManagerName); err != nil { + return ctrl.Result{}, true, errors.Wrapf(err, "failed to clean up managedFields of BootstrapConfig %s", klog.KObj(bootstrapConfig)) + } + // Update in-place mutating fields on BootstrapConfig. + if err := r.updateLabelsAndAnnotations(ctx, bootstrapConfig, machineSet); err != nil { + return ctrl.Result{}, true, errors.Wrapf(err, "failed to update BootstrapConfig %s", klog.KObj(bootstrapConfig)) + } } } } - return ctrl.Result{}, nil -} - -func newMachineUpToDateCondition(s *scope) *metav1.Condition { - // If the current MachineSet is a stand-alone MachineSet, the MachineSet controller does not set an up-to-date condition - // on Machines, allowing tools managing higher level abstractions to set this condition. - // This is also consistent with the fact that the MachineSet controller primarily takes care of the number of Machine - // replicas, it doesn't reconcile them (even if we have a few exceptions like in-place propagation of a few selected - // fields and remediation). - if s.owningMachineDeployment == nil { - return nil - } - - // Determine current and desired state. - // If the current MachineSet is owned by a MachineDeployment, we mirror what is implemented in the MachineDeployment controller - // to trigger rollouts (by creating new MachineSets). - // More specifically: - // - desired state for the Machine is the spec.Template of the MachineDeployment - // - current state for the Machine is the spec.Template of the MachineSet who owns the Machine - // Note: We are intentionally considering current spec from the MachineSet instead of spec from the Machine itself in - // order to surface info consistent with what the MachineDeployment controller uses to take decisions about rollouts. - // The downside is that the system will ignore out of band changes applied to controlled Machines, which is - // considered an acceptable trade-off given that out of band changes are the exception (users should not change - // objects owned by the system). - // However, if out of band changes happen, at least the system will ignore out of band changes consistently, both in the - // MachineDeployment controller and in the condition computed here. - current := &s.machineSet.Spec.Template - desired := &s.owningMachineDeployment.Spec.Template - - upToDate, _, conditionMessages := mdutil.MachineTemplateUpToDate(current, desired) - - if !s.owningMachineDeployment.Spec.Rollout.After.IsZero() { - if s.owningMachineDeployment.Spec.Rollout.After.Time.Before(s.reconciliationTime) && !s.machineSet.CreationTimestamp.After(s.owningMachineDeployment.Spec.Rollout.After.Time) { - upToDate = false - conditionMessages = append(conditionMessages, "MachineDeployment spec.rolloutAfter expired") - } - } - if !upToDate { - for i := range conditionMessages { - conditionMessages[i] = fmt.Sprintf("* %s", conditionMessages[i]) - } - return &metav1.Condition{ - Type: clusterv1.MachineUpToDateCondition, - Status: metav1.ConditionFalse, - Reason: clusterv1.MachineNotUpToDateReason, - // Note: the code computing the message for MachineDeployment's RolloutOut condition is making assumptions on the format/content of this message. - Message: strings.Join(conditionMessages, "\n"), - } + if anyManagedFieldIssueMitigated { + return ctrl.Result{RequeueAfter: 1 * time.Second}, true, nil // Explicitly requeue as we are not watching for changes to BootstrapConfig and InfraMachine objects. } - return &metav1.Condition{ - Type: clusterv1.MachineUpToDateCondition, - Status: metav1.ConditionTrue, - Reason: clusterv1.MachineUpToDateReason, - } + return ctrl.Result{}, false, nil } // syncReplicas scales Machine resources up or down. func (r *Reconciler) syncReplicas(ctx context.Context, s *scope) (ctrl.Result, error) { ms := s.machineSet machines := s.machines - cluster := s.cluster - if !s.getAndAdoptMachinesForMachineSetSucceeded { - return ctrl.Result{}, nil - } log := ctrl.LoggerFrom(ctx) if ms.Spec.Replicas == nil { return ctrl.Result{}, errors.Errorf("the Replicas field in Spec for MachineSet %v is nil, this should not be allowed", ms.Name) } - diff := len(machines) - int(*(ms.Spec.Replicas)) + diff := len(machines) - int(ptr.Deref(ms.Spec.Replicas, 0)) switch { case diff < 0: - diff *= -1 - log.Info(fmt.Sprintf("MachineSet is scaling up to %d replicas by creating %d machines", *(ms.Spec.Replicas), diff), "replicas", *(ms.Spec.Replicas), "machineCount", len(machines)) + // If there are not enough Machines, create missing Machines unless Machine creation is disabled. + machinesToAdd := -diff + log.Info(fmt.Sprintf("MachineSet is scaling up to %d replicas by creating %d Machines", *(ms.Spec.Replicas), machinesToAdd), "replicas", *(ms.Spec.Replicas), "machineCount", len(machines)) if ms.Annotations != nil { - if _, ok := ms.Annotations[clusterv1.DisableMachineCreateAnnotation]; ok { - log.Info("Automatic creation of new machines disabled for machine set") + if value, ok := ms.Annotations[clusterv1.DisableMachineCreateAnnotation]; ok && value == "true" { + log.Info("Automatic creation of new machines disabled for MachineSet") return ctrl.Result{}, nil } } + return r.createMachines(ctx, s, machinesToAdd) - preflightCheckErrMessages, err := r.runPreflightChecks(ctx, cluster, ms, "Scale up") - if err != nil || len(preflightCheckErrMessages) > 0 { - if err != nil { - // If err is not nil use that as the preflightCheckErrMessage - preflightCheckErrMessages = append(preflightCheckErrMessages, err.Error()) + case diff > 0: + // if too many replicas, delete or move exceeding machines. + + // If the MachineSet is accepting replicas from other MachineSets (and thus this is the newMS controlled by a MD), + // detect if there are replicas still pending AcknowledgedMove. + // Note: replicas still pending AcknowledgeMove should not be counted when computing the numbers of machines to delete, because those machines are not included in ms.Spec.Replicas yet. + // Without this check, the following logic would try to align the number of replicas to "an incomplete" ms.Spec.Replicas and as a consequence wrongly delete replicas that should be preserved. + notAcknowledgeMoveReplicas := sets.Set[string]{} + if sourceMSs, ok := ms.Annotations[clusterv1.MachineSetReceiveMachinesFromMachineSetsAnnotation]; ok && sourceMSs != "" { + for _, m := range machines { + if _, ok := m.Annotations[clusterv1.PendingAcknowledgeMoveAnnotation]; !ok { + continue + } + notAcknowledgeMoveReplicas.Insert(m.Name) } + } + if notAcknowledgeMoveReplicas.Len() > 0 { + log.V(5).Info(fmt.Sprintf("Machines %s moved from an old MachineSet still pending acknowledge from MachineDeployment", notAcknowledgeMoveReplicas.UnsortedList())) + } - s.scaleUpPreflightCheckErrMessages = preflightCheckErrMessages - v1beta1conditions.MarkFalse(ms, clusterv1.MachinesCreatedV1Beta1Condition, clusterv1.PreflightCheckFailedV1Beta1Reason, clusterv1.ConditionSeverityError, "%s", strings.Join(preflightCheckErrMessages, "; ")) - if err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{RequeueAfter: preflightFailedRequeueAfter}, nil + machinesToDeleteOrMove := len(machines) - notAcknowledgeMoveReplicas.Len() - int(ptr.Deref(ms.Spec.Replicas, 0)) + if machinesToDeleteOrMove == 0 { + return ctrl.Result{}, nil + } + + // Move machines to the target MachineSet if the current MachineSet is instructed to do so. + if targetMSName, ok := ms.Annotations[clusterv1.MachineSetMoveMachinesToMachineSetAnnotation]; ok && targetMSName != "" { + // Note: The number of machines actually moved could be less than expected e.g. because some machine still updating in-place from a previous move. + return r.startMoveMachines(ctx, s, targetMSName, machinesToDeleteOrMove) + } + + // Otherwise the current MachineSet is not instructed to move machines to another MachineSet, + // then delete all the exceeding machines. + return r.deleteMachines(ctx, s, machinesToDeleteOrMove) + } + + return ctrl.Result{}, nil +} + +func (r *Reconciler) createMachines(ctx context.Context, s *scope, machinesToAdd int) (ctrl.Result, error) { + if r.overrideCreateMachines != nil { + return r.overrideCreateMachines(ctx, s, machinesToAdd) + } + + log := ctrl.LoggerFrom(ctx) + ms := s.machineSet + cluster := s.cluster + + preflightCheckErrMessages, err := r.runPreflightChecks(ctx, cluster, ms, "Scale up") + if err != nil || len(preflightCheckErrMessages) > 0 { + if err != nil { + // If err is not nil use that as the preflightCheckErrMessage + preflightCheckErrMessages = append(preflightCheckErrMessages, err.Error()) + } + + s.scaleUpPreflightCheckErrMessages = preflightCheckErrMessages + v1beta1conditions.MarkFalse(ms, clusterv1.MachinesCreatedV1Beta1Condition, clusterv1.PreflightCheckFailedV1Beta1Reason, clusterv1.ConditionSeverityError, "%s", strings.Join(preflightCheckErrMessages, "; ")) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{RequeueAfter: preflightFailedRequeueAfter}, nil + } + + machinesAdded := []*clusterv1.Machine{} + for i := range machinesToAdd { + // Create a new logger so the global logger is not modified. + log := log + machine, computeMachineErr := r.computeDesiredMachine(ms, nil) + if computeMachineErr != nil { + v1beta1conditions.MarkFalse(ms, clusterv1.MachinesCreatedV1Beta1Condition, clusterv1.MachineCreationFailedV1Beta1Reason, + clusterv1.ConditionSeverityError, "%s", computeMachineErr.Error()) + return ctrl.Result{}, errors.Wrap(computeMachineErr, "failed to create Machine: failed to compute desired Machine") } var ( - machineList []*clusterv1.Machine - errs []error + infraRef, bootstrapRef clusterv1.ContractVersionedObjectReference + infraMachine, bootstrapConfig *unstructured.Unstructured ) - for i := range diff { - // Create a new logger so the global logger is not modified. - log := log - machine, computeMachineErr := r.computeDesiredMachine(ms, nil) - if computeMachineErr != nil { - return ctrl.Result{}, errors.Wrap(computeMachineErr, "failed to create Machine: failed to compute desired Machine") + // Create the BootstrapConfig if necessary. + if ms.Spec.Template.Spec.Bootstrap.ConfigRef.IsDefined() { + bootstrapConfig, bootstrapRef, err = r.createBootstrapConfig(ctx, ms, machine) + if err != nil { + v1beta1conditions.MarkFalse(ms, clusterv1.MachinesCreatedV1Beta1Condition, clusterv1.BootstrapTemplateCloningFailedV1Beta1Reason, clusterv1.ConditionSeverityError, "%s", err.Error()) + return ctrl.Result{}, errors.Wrapf(err, "failed to clone bootstrap configuration from %s %s while creating a Machine", + ms.Spec.Template.Spec.Bootstrap.ConfigRef.Kind, + klog.KRef(ms.Namespace, ms.Spec.Template.Spec.Bootstrap.ConfigRef.Name)) } - // Clone and set the infrastructure and bootstrap references. - var ( - infraRef, bootstrapRef clusterv1.ContractVersionedObjectReference - infraMachine, bootstrapConfig *unstructured.Unstructured - ) - - // Create the BootstrapConfig if necessary. - if ms.Spec.Template.Spec.Bootstrap.ConfigRef.IsDefined() { - apiVersion, err := contract.GetAPIVersion(ctx, r.Client, ms.Spec.Template.Spec.Bootstrap.ConfigRef.GroupKind()) - if err == nil { - bootstrapConfig, bootstrapRef, err = external.CreateFromTemplate(ctx, &external.CreateFromTemplateInput{ - Client: r.Client, - TemplateRef: &corev1.ObjectReference{ - APIVersion: apiVersion, - Kind: ms.Spec.Template.Spec.Bootstrap.ConfigRef.Kind, - Namespace: ms.Namespace, - Name: ms.Spec.Template.Spec.Bootstrap.ConfigRef.Name, - }, - Namespace: machine.Namespace, - Name: machine.Name, - ClusterName: machine.Spec.ClusterName, - Labels: machine.Labels, - Annotations: machine.Annotations, - OwnerRef: &metav1.OwnerReference{ - APIVersion: clusterv1.GroupVersion.String(), - Kind: "MachineSet", - Name: ms.Name, - UID: ms.UID, - }, - }) - } - if err != nil { - v1beta1conditions.MarkFalse(ms, clusterv1.MachinesCreatedV1Beta1Condition, clusterv1.BootstrapTemplateCloningFailedV1Beta1Reason, clusterv1.ConditionSeverityError, "%s", err.Error()) - return ctrl.Result{}, errors.Wrapf(err, "failed to clone bootstrap configuration from %s %s while creating a Machine", - ms.Spec.Template.Spec.Bootstrap.ConfigRef.Kind, - klog.KRef(ms.Namespace, ms.Spec.Template.Spec.Bootstrap.ConfigRef.Name)) + machine.Spec.Bootstrap.ConfigRef = bootstrapRef + log = log.WithValues(bootstrapRef.Kind, klog.KRef(ms.Namespace, bootstrapRef.Name)) + } + + // Create the InfraMachine. + infraMachine, infraRef, err = r.createInfraMachine(ctx, ms, machine) + if err != nil { + var deleteErr error + if bootstrapRef.IsDefined() { + // Cleanup the bootstrap resource if we can't create the InfraMachine; or we might risk to leak it. + if err := r.Client.Delete(ctx, bootstrapConfig); err != nil && !apierrors.IsNotFound(err) { + deleteErr = errors.Wrapf(err, "failed to cleanup %s %s after %s creation failed", bootstrapRef.Kind, klog.KRef(ms.Namespace, bootstrapRef.Name), ms.Spec.Template.Spec.InfrastructureRef.Kind) } - machine.Spec.Bootstrap.ConfigRef = bootstrapRef - log = log.WithValues(bootstrapRef.Kind, klog.KRef(ms.Namespace, bootstrapRef.Name)) } - // Create the InfraMachine. - apiVersion, err := contract.GetAPIVersion(ctx, r.Client, ms.Spec.Template.Spec.InfrastructureRef.GroupKind()) - if err == nil { - infraMachine, infraRef, err = external.CreateFromTemplate(ctx, &external.CreateFromTemplateInput{ - Client: r.Client, - TemplateRef: &corev1.ObjectReference{ - APIVersion: apiVersion, - Kind: ms.Spec.Template.Spec.InfrastructureRef.Kind, - Namespace: ms.Namespace, - Name: ms.Spec.Template.Spec.InfrastructureRef.Name, - }, - - Namespace: machine.Namespace, - Name: machine.Name, - ClusterName: machine.Spec.ClusterName, - Labels: machine.Labels, - Annotations: machine.Annotations, - OwnerRef: &metav1.OwnerReference{ - APIVersion: clusterv1.GroupVersion.String(), - Kind: "MachineSet", - Name: ms.Name, - UID: ms.UID, - }, - }) + v1beta1conditions.MarkFalse(ms, clusterv1.MachinesCreatedV1Beta1Condition, clusterv1.InfrastructureTemplateCloningFailedV1Beta1Reason, clusterv1.ConditionSeverityError, "%s", err.Error()) + return ctrl.Result{}, kerrors.NewAggregate([]error{errors.Wrapf(err, "failed to clone infrastructure machine from %s %s while creating a Machine", + ms.Spec.Template.Spec.InfrastructureRef.Kind, + klog.KRef(ms.Namespace, ms.Spec.Template.Spec.InfrastructureRef.Name)), deleteErr}) + } + log = log.WithValues(infraRef.Kind, klog.KRef(ms.Namespace, infraRef.Name)) + machine.Spec.InfrastructureRef = infraRef + + // Create the Machine. + if err := ssa.Patch(ctx, r.Client, machineSetManagerName, machine); err != nil { + // Try to cleanup the external objects if the Machine creation failed. + errs := []error{err} + if err := r.Client.Delete(ctx, infraMachine); !apierrors.IsNotFound(err) { + errs = append(errs, errors.Wrapf(err, "failed to cleanup %s %s after Machine creation failed", infraRef.Kind, klog.KRef(ms.Namespace, infraRef.Name))) } - if err != nil { - var deleteErr error - if bootstrapRef.IsDefined() { - // Cleanup the bootstrap resource if we can't create the InfraMachine; or we might risk to leak it. - if err := r.Client.Delete(ctx, bootstrapConfig); err != nil && !apierrors.IsNotFound(err) { - deleteErr = errors.Wrapf(err, "failed to cleanup %s %s after %s creation failed", bootstrapRef.Kind, klog.KRef(ms.Namespace, bootstrapRef.Name), ms.Spec.Template.Spec.InfrastructureRef.Kind) - } + if bootstrapRef.IsDefined() { + if err := r.Client.Delete(ctx, bootstrapConfig); !apierrors.IsNotFound(err) { + errs = append(errs, errors.Wrapf(err, "failed to cleanup %s %s after Machine creation failed", bootstrapRef.Kind, klog.KRef(ms.Namespace, bootstrapRef.Name))) } - v1beta1conditions.MarkFalse(ms, clusterv1.MachinesCreatedV1Beta1Condition, clusterv1.InfrastructureTemplateCloningFailedV1Beta1Reason, clusterv1.ConditionSeverityError, "%s", err.Error()) - return ctrl.Result{}, kerrors.NewAggregate([]error{errors.Wrapf(err, "failed to clone infrastructure machine from %s %s while creating a Machine", - ms.Spec.Template.Spec.InfrastructureRef.Kind, - klog.KRef(ms.Namespace, ms.Spec.Template.Spec.InfrastructureRef.Name)), deleteErr}) } - log = log.WithValues(infraRef.Kind, klog.KRef(ms.Namespace, infraRef.Name)) - machine.Spec.InfrastructureRef = infraRef - // Create the Machine. - if err := ssa.Patch(ctx, r.Client, machineSetManagerName, machine); err != nil { - log.Error(err, "Error while creating a machine") - r.recorder.Eventf(ms, corev1.EventTypeWarning, "FailedCreate", "Failed to create machine: %v", err) - errs = append(errs, err) - v1beta1conditions.MarkFalse(ms, clusterv1.MachinesCreatedV1Beta1Condition, clusterv1.MachineCreationFailedV1Beta1Reason, - clusterv1.ConditionSeverityError, "%s", err.Error()) + v1beta1conditions.MarkFalse(ms, clusterv1.MachinesCreatedV1Beta1Condition, clusterv1.MachineCreationFailedV1Beta1Reason, + clusterv1.ConditionSeverityError, "%s", err.Error()) + return ctrl.Result{}, kerrors.NewAggregate(errs) + } - // Try to cleanup the external objects if the Machine creation failed. - if err := r.Client.Delete(ctx, infraMachine); !apierrors.IsNotFound(err) { - log.Error(err, "Failed to cleanup infrastructure machine object after Machine creation error", infraRef.Kind, klog.KRef(ms.Namespace, infraRef.Name)) - } - if bootstrapRef.IsDefined() { - if err := r.Client.Delete(ctx, bootstrapConfig); !apierrors.IsNotFound(err) { - log.Error(err, "Failed to cleanup bootstrap configuration object after Machine creation error", bootstrapRef.Kind, klog.KRef(ms.Namespace, bootstrapRef.Name)) - } - } + machinesAdded = append(machinesAdded, machine) + log.Info(fmt.Sprintf("Machine %s created (scale up, creating %d of %d)", machine.Name, i+1, machinesToAdd), "Machine", klog.KObj(machine)) + r.recorder.Eventf(ms, corev1.EventTypeNormal, "SuccessfulCreate", "Created Machine %q", machine.Name) + } + + // Wait for cache update to ensure following reconcile gets latest change. + return ctrl.Result{}, clientutil.WaitForObjectsToBeAddedToTheCache(ctx, r.Client, "Machine creation", machinesAdded...) +} + +func (r *Reconciler) deleteMachines(ctx context.Context, s *scope, machinesToDelete int) (ctrl.Result, error) { + if r.overrideDeleteMachines != nil { + return r.overrideDeleteMachines(ctx, s, machinesToDelete) + } + + log := ctrl.LoggerFrom(ctx) + ms := s.machineSet + machines := s.machines + + log.Info(fmt.Sprintf("MachineSet is scaling down to %d replicas by deleting %d Machines", *(ms.Spec.Replicas), machinesToDelete), "replicas", *(ms.Spec.Replicas), "machineCount", len(machines), "order", cmp.Or(ms.Spec.Deletion.Order, clusterv1.RandomMachineSetDeletionOrder)) + + // Pick the list of machines to be deleted according to the criteria defined in ms.Spec.Deletion.Order. + // Note: In case the system has to delete some machine, and there are machines are still updating in place, those machine must be deleted first. + // + // This prevents the system to perform unnecessary in-place updates. e.g. + // - In place rollout of MD with 3 Replicas, maxSurge 1, MaxUnavailable 0 + // - First create m4 to create a buffer for doing in place + // - Move old machines (m1, m2, m3) + // - Resulting new MS at this point has 4 replicas m1, m2, m3 (updating in place) and (m4). + // - The system scales down MS, and the system does this getting rid of m3 - the last replica that started in place. + deletePriorityFunc, err := getDeletePriorityFunc(ms) + if err != nil { + return ctrl.Result{}, err + } + machinesToDeleteByPriority := getMachinesToDeletePrioritized(machines, machinesToDelete, deletePriorityFunc) + + var errs []error + machinesDeleted := []*clusterv1.Machine{} + for i, machine := range machinesToDeleteByPriority { + log := log.WithValues("Machine", klog.KObj(machine)) + if machine.GetDeletionTimestamp().IsZero() { + if err := r.Client.Delete(ctx, machine); err != nil { + errs = append(errs, err) continue } - log.Info(fmt.Sprintf("Machine created (scale up, creating %d of %d)", i+1, diff), "Machine", klog.KObj(machine)) - r.recorder.Eventf(ms, corev1.EventTypeNormal, "SuccessfulCreate", "Created machine %q", machine.Name) - machineList = append(machineList, machine) + machinesDeleted = append(machinesDeleted, machine) + log.Info(fmt.Sprintf("Machine %s deleting (scale down, deleting %d of %d)", machine.Name, i+1, machinesToDelete)) + r.recorder.Eventf(ms, corev1.EventTypeNormal, "SuccessfulDelete", "Deleted Machine %q", machine.Name) + } else { + log.Info(fmt.Sprintf("Waiting for Machine to be deleted (scale down, deleting %d of %d)", i+1, machinesToDelete)) } + } - if len(errs) > 0 { - return ctrl.Result{}, kerrors.NewAggregate(errs) + // Wait for cache update to ensure following reconcile gets latest change. + if err := clientutil.WaitForObjectsToBeDeletedFromTheCache(ctx, r.Client, "Machine deletion (scale down)", machinesDeleted...); err != nil { + errs = append(errs, err) + } + if len(errs) > 0 { + return ctrl.Result{}, kerrors.NewAggregate(errs) + } + return ctrl.Result{}, nil +} + +func (r *Reconciler) startMoveMachines(ctx context.Context, s *scope, targetMSName string, machinesToMove int) (ctrl.Result, error) { + if r.overrideMoveMachines != nil { + return r.overrideMoveMachines(ctx, s, targetMSName, machinesToMove) + } + + log := ctrl.LoggerFrom(ctx) + ms := s.machineSet + machines := s.machines + + // Check that everything is set for the move operation by validating that the target MS is expecting to + // receive replicas from the current one. + targetMS := &clusterv1.MachineSet{} + if err := r.Client.Get(ctx, client.ObjectKey{Name: targetMSName, Namespace: ms.Namespace}, targetMS); err != nil { + return ctrl.Result{}, errors.Wrapf(err, "failed to get MachineSet %s, which is the target for the move operation", targetMSName) + } + + validSourceMSs := targetMS.Annotations[clusterv1.MachineSetReceiveMachinesFromMachineSetsAnnotation] + sourcesSet := sets.Set[string]{} + sourcesSet.Insert(strings.Split(validSourceMSs, ",")...) + if !sourcesSet.Has(ms.Name) { + return ctrl.Result{}, errors.Errorf("MachineSet %s is set to move replicas to %s, but %[2]s only accepts Machines from %s", ms.Name, targetMS.Name, validSourceMSs) + } + + log.Info(fmt.Sprintf("MachineSet is scaling down to %d replicas by moving %d Machines to %s", *(ms.Spec.Replicas), machinesToMove, targetMSName), "replicas", *(ms.Spec.Replicas), "machineCount", len(machines), "order", cmp.Or(ms.Spec.Deletion.Order, clusterv1.RandomMachineSetDeletionOrder)) + + // Sort to Move machine in deterministic and predictable order. + // Note: For convenience we sort machine using the ordering criteria defined in ms.Spec.Deletion.Order. + deletePriorityFunc, err := getDeletePriorityFunc(ms) + if err != nil { + return ctrl.Result{}, err + } + machinesToMoveByPriority := getMachinesToMovePrioritized(machines, deletePriorityFunc) + + errs := []error{} + machinesMoved := []*clusterv1.Machine{} + for _, machine := range machinesToMoveByPriority { + if machinesToMove <= 0 { + break } - return ctrl.Result{}, r.waitForMachineCreation(ctx, machineList) - case diff > 0: - log.Info(fmt.Sprintf("MachineSet is scaling down to %d replicas by deleting %d machines", *(ms.Spec.Replicas), diff), "replicas", *(ms.Spec.Replicas), "machineCount", len(machines), "order", cmp.Or(ms.Spec.Deletion.Order, clusterv1.RandomMachineSetDeletionOrder)) - deletePriorityFunc, err := getDeletePriorityFunc(ms) - if err != nil { - return ctrl.Result{}, err + log := log.WithValues("Machine", klog.KObj(machine)) + + // Make sure we are not moving machines still updating in place from a previous move (this includes also machines still pending AcknowledgeMove). + if inplace.IsUpdateInProgress(machine) { + continue } - var errs []error - machinesToDelete := getMachinesToDeletePrioritized(machines, diff, deletePriorityFunc) - for i, machine := range machinesToDelete { - log := log.WithValues("Machine", klog.KObj(machine)) - if machine.GetDeletionTimestamp().IsZero() { - if err := r.Client.Delete(ctx, machine); err != nil { - log.Error(err, "Unable to delete Machine") - r.recorder.Eventf(ms, corev1.EventTypeWarning, "FailedDelete", "Failed to delete machine %q: %v", machine.Name, err) - errs = append(errs, err) - continue - } - // Note: We intentionally log after Delete because we want this log line to show up only after DeletionTimestamp has been set. - // Also, setting DeletionTimestamp doesn't mean the Machine is actually deleted (deletion takes some time). - log.Info(fmt.Sprintf("Deleting Machine (scale down, deleting %d of %d)", i+1, diff)) - r.recorder.Eventf(ms, corev1.EventTypeNormal, "SuccessfulDelete", "Deleted machine %q", machine.Name) - } else { - log.Info(fmt.Sprintf("Waiting for Machine to be deleted (scale down, deleting %d of %d)", i+1, diff)) - } + // Make sure we are not moving machines not yet provisioned. + if !machine.Status.NodeRef.IsDefined() { + continue } - if len(errs) > 0 { - return ctrl.Result{}, kerrors.NewAggregate(errs) + // Note. Machines with the DeleteMachineAnnotation are going to be moved and the new MS + // will take care of fulfilling this intent as soon as it scales down. + // Note. Also Machines marked as unhealthy by MHC are going to be moved, because otherwise + // remediation will not complete as the Machine is owned by an old MS. + + machinesToMove-- + + // If there are machines already deleting, wait for them to go away before further scaling down with move. + if !machine.DeletionTimestamp.IsZero() { + continue + } + + log.Info(fmt.Sprintf("Triggering in-place update for Machine %s (by moving to MachineSet %s)", machine.Name, targetMS.Name)) + + // Perform the first part of a move operation, the part under the responsibility of the oldMS. + orig := machine.DeepCopy() + + // Change the ownerReference from current MS to target MS + // Note: After move, when the first reconcile of the target MS will happen, there will be co-ownership on + // this ownerReference between "manager" and "capi-machineset", but this is not an issue because the MS controller will never unset this field. + machine.OwnerReferences = util.ReplaceOwnerRef(machine.OwnerReferences, ms, metav1.OwnerReference{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "MachineSet", + Name: targetMS.Name, + UID: targetMS.UID, + Controller: ptr.To(true), + }) + + // Change machine's labels from current MS to target MS + // Note: The implementation assumes that current and target MS are originated from the same MachineDeployment, + // and thus this change can be performed in a surgical way by patching only the MachineDeploymentUniqueLabel. + // Notably, minimizing label changes, simplifies impacts and considerations about managedFields and co-ownership. + // Note: After move, when the first reconcile of the target MS will happen, there will be co-ownership on + // this label between "manager" and "capi-machineset", but this is not an issue because the MS controller will never unset this label. + targetUniqueLabel, ok := targetMS.Labels[clusterv1.MachineDeploymentUniqueLabel] + if !ok { + return ctrl.Result{}, errors.Errorf("MachineSet %s does not have the %s label", targetMS.Name, clusterv1.MachineDeploymentUniqueLabel) + } + machine.Labels[clusterv1.MachineDeploymentUniqueLabel] = targetUniqueLabel + + // Apply the UpdateInProgress and PendingAcknowledgeMove annotation + if machine.Annotations == nil { + machine.Annotations = map[string]string{} + } + machine.Annotations[clusterv1.UpdateInProgressAnnotation] = "" + machine.Annotations[clusterv1.PendingAcknowledgeMoveAnnotation] = "" + + // Note: Intentionally using client.Patch instead of SSA. Otherwise we would have to ensure we preserve + // UpdateInProgressAnnotation on existing Machines and that would lead to race conditions when + // the Machine controller tries to remove the annotation and then the MachineSet controller adds it back. + if err := r.Client.Patch(ctx, machine, client.MergeFrom(orig)); err != nil { + errs = append(errs, errors.Wrapf(err, "failed to trigger in-place update for Machine %s by moving to MachineSet %s", klog.KObj(machine), klog.KObj(targetMS))) + continue } - return ctrl.Result{}, r.waitForMachineDeletion(ctx, machinesToDelete) + machinesMoved = append(machinesMoved, machine) } + // Wait for cache update to ensure following reconcile gets latest change. + if err := clientutil.WaitForCacheToBeUpToDate(ctx, r.Client, "moving Machines", machinesMoved...); err != nil { + errs = append(errs, err) + } + if len(errs) > 0 { + return ctrl.Result{}, kerrors.NewAggregate(errs) + } return ctrl.Result{}, nil } @@ -930,8 +1158,11 @@ func (r *Reconciler) computeDesiredMachine(machineSet *clusterv1.MachineSet, exi } desiredMachine.Finalizers = finalizers + // Make sure sync machines do not change the fields that are not in-place mutable desiredMachine.Spec.Bootstrap.ConfigRef = existingMachine.Spec.Bootstrap.ConfigRef desiredMachine.Spec.InfrastructureRef = existingMachine.Spec.InfrastructureRef + desiredMachine.Spec.Version = existingMachine.Spec.Version + desiredMachine.Spec.FailureDomain = existingMachine.Spec.FailureDomain } // Set the in-place mutable fields. // When we create a new Machine we will just create the Machine with those fields. @@ -949,13 +1180,14 @@ func (r *Reconciler) computeDesiredMachine(machineSet *clusterv1.MachineSet, exi desiredMachine.Spec.Deletion.NodeDeletionTimeoutSeconds = machineSet.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds desiredMachine.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = machineSet.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds desiredMachine.Spec.MinReadySeconds = machineSet.Spec.Template.Spec.MinReadySeconds + desiredMachine.Spec.Taints = machineSet.Spec.Template.Spec.Taints return desiredMachine, nil } -// updateExternalObject updates the external object passed in with the +// updateLabelsAndAnnotations updates the external object passed in with the // updated labels and annotations from the MachineSet. -func (r *Reconciler) updateExternalObject(ctx context.Context, obj client.Object, machineSet *clusterv1.MachineSet) error { +func (r *Reconciler) updateLabelsAndAnnotations(ctx context.Context, obj client.Object, machineSet *clusterv1.MachineSet) error { updatedObject := &unstructured.Unstructured{} updatedObject.SetGroupVersionKind(obj.GetObjectKind().GroupVersionKind()) updatedObject.SetNamespace(obj.GetNamespace()) @@ -967,10 +1199,7 @@ func (r *Reconciler) updateExternalObject(ctx context.Context, obj client.Object updatedObject.SetLabels(machineLabelsFromMachineSet(machineSet)) updatedObject.SetAnnotations(machineAnnotationsFromMachineSet(machineSet)) - if err := ssa.Patch(ctx, r.Client, machineSetManagerName, updatedObject, ssa.WithCachingProxy{Cache: r.ssaCache, Original: obj}); err != nil { - return errors.Wrapf(err, "failed to update %s", klog.KObj(obj)) - } - return nil + return ssa.Patch(ctx, r.Client, machineSetMetadataManagerName, updatedObject, ssa.WithCachingProxy{Cache: r.ssaCache, Original: obj}) } func (r *Reconciler) getOwnerMachineDeployment(ctx context.Context, machineSet *clusterv1.MachineSet) (*clusterv1.MachineDeployment, error) { @@ -1034,55 +1263,6 @@ func (r *Reconciler) adoptOrphan(ctx context.Context, machineSet *clusterv1.Mach return r.Client.Patch(ctx, machine, patch) } -func (r *Reconciler) waitForMachineCreation(ctx context.Context, machineList []*clusterv1.Machine) error { - log := ctrl.LoggerFrom(ctx) - - for i := range machineList { - machine := machineList[i] - pollErr := wait.PollUntilContextTimeout(ctx, stateConfirmationInterval, stateConfirmationTimeout, true, func(ctx context.Context) (bool, error) { - key := client.ObjectKey{Namespace: machine.Namespace, Name: machine.Name} - if err := r.Client.Get(ctx, key, &clusterv1.Machine{}); err != nil { - if apierrors.IsNotFound(err) { - return false, nil - } - return false, err - } - - return true, nil - }) - - if pollErr != nil { - log.Error(pollErr, "Failed waiting for machine object to be created") - return errors.Wrap(pollErr, "failed waiting for machine object to be created") - } - } - - return nil -} - -func (r *Reconciler) waitForMachineDeletion(ctx context.Context, machineList []*clusterv1.Machine) error { - log := ctrl.LoggerFrom(ctx) - - for i := range machineList { - machine := machineList[i] - pollErr := wait.PollUntilContextTimeout(ctx, stateConfirmationInterval, stateConfirmationTimeout, true, func(ctx context.Context) (bool, error) { - m := &clusterv1.Machine{} - key := client.ObjectKey{Namespace: machine.Namespace, Name: machine.Name} - err := r.Client.Get(ctx, key, m) - if apierrors.IsNotFound(err) || !m.DeletionTimestamp.IsZero() { - return true, nil - } - return false, err - }) - - if pollErr != nil { - log.Error(pollErr, "Failed waiting for machine object to be deleted") - return errors.Wrap(pollErr, "failed waiting for machine object to be deleted") - } - } - return nil -} - // MachineToMachineSets is a handler.ToRequestsFunc to be used to enqueue requests for reconciliation // for MachineSets that might adopt an orphaned Machine. func (r *Reconciler) MachineToMachineSets(ctx context.Context, o client.Object) []ctrl.Request { @@ -1253,7 +1433,7 @@ func (r *Reconciler) reconcileV1Beta1Status(ctx context.Context, s *scope) error // are actually provisioned (vs reporting completed immediately after the last machine object is created). This convention is also used by KCP. if ms.Status.Deprecated.V1Beta1.ReadyReplicas == currentReplicas { if v1beta1conditions.IsFalse(ms, clusterv1.ResizedV1Beta1Condition) { - log.Info("All the replicas are ready", "replicas", ms.Status.Deprecated.V1Beta1.ReadyReplicas) + log.V(5).Info("All the replicas are ready", "replicas", ms.Status.Deprecated.V1Beta1.ReadyReplicas) } v1beta1conditions.MarkTrue(ms, clusterv1.ResizedV1Beta1Condition) } @@ -1311,10 +1491,6 @@ func (r *Reconciler) getMachineNode(ctx context.Context, cluster *clusterv1.Clus } func (r *Reconciler) reconcileUnhealthyMachines(ctx context.Context, s *scope) (ctrl.Result, error) { - if !s.getAndAdoptMachinesForMachineSetSucceeded { - return ctrl.Result{}, nil - } - cluster := s.cluster ms := s.machineSet machines := s.machines @@ -1514,7 +1690,7 @@ func (r *Reconciler) reconcileUnhealthyMachines(ctx context.Context, s *scope) ( } // Note: We intentionally log after Delete because we want this log line to show up only after DeletionTimestamp has been set. // Also, setting DeletionTimestamp doesn't mean the Machine is actually deleted (deletion takes some time). - log.Info("Deleting Machine (remediating unhealthy Machine)", "Machine", klog.KObj(m)) + log.Info(fmt.Sprintf("Deleting Machine %s (remediating unhealthy Machine)", m.Name), "Machine", klog.KObj(m)) } if len(errs) > 0 { return ctrl.Result{}, errors.Wrapf(kerrors.NewAggregate(errs), "failed to delete unhealthy Machines") @@ -1601,6 +1777,162 @@ func (r *Reconciler) reconcileExternalTemplateReference(ctx context.Context, clu return false, patchHelper.Patch(ctx, obj) } +func (r *Reconciler) createBootstrapConfig(ctx context.Context, ms *clusterv1.MachineSet, machine *clusterv1.Machine) (*unstructured.Unstructured, clusterv1.ContractVersionedObjectReference, error) { + bootstrapConfig, err := r.computeDesiredBootstrapConfig(ctx, ms, machine, nil) + if err != nil { + return nil, clusterv1.ContractVersionedObjectReference{}, errors.Wrapf(err, "failed to create BootstrapConfig") + } + + // Create the full object with capi-machineset. + // Below ssa.RemoveManagedFieldsForLabelsAndAnnotations will drop ownership for labels and annotations + // so that in a subsequent syncMachines call capi-machineset-metadata can take ownership for them. + // Note: This is done in way that it does not rely on managedFields being stored in the cache, so we can optimize + // memory usage by dropping managedFields before storing objects in the cache. + if err := ssa.Patch(ctx, r.Client, machineSetManagerName, bootstrapConfig); err != nil { + return nil, clusterv1.ContractVersionedObjectReference{}, errors.Wrapf(err, "failed to create BootstrapConfig") + } + + // Note: This field is only used for unit tests that use fake client because the fake client does not properly set resourceVersion + // on BootstrapConfig/InfraMachine after ssa.Patch and then ssa.RemoveManagedFieldsForLabelsAndAnnotations would fail. + if !r.disableRemoveManagedFieldsForLabelsAndAnnotations { + if err := ssa.RemoveManagedFieldsForLabelsAndAnnotations(ctx, r.Client, r.APIReader, bootstrapConfig, machineSetManagerName); err != nil { + return nil, clusterv1.ContractVersionedObjectReference{}, errors.Wrapf(err, "failed to create BootstrapConfig") + } + } + + return bootstrapConfig, clusterv1.ContractVersionedObjectReference{ + APIGroup: bootstrapConfig.GroupVersionKind().Group, + Kind: bootstrapConfig.GetKind(), + Name: bootstrapConfig.GetName(), + }, nil +} + +func (r *Reconciler) computeDesiredBootstrapConfig(ctx context.Context, ms *clusterv1.MachineSet, machine *clusterv1.Machine, existingBootstrapConfig *unstructured.Unstructured) (*unstructured.Unstructured, error) { + var ownerReference *metav1.OwnerReference + if existingBootstrapConfig == nil || !util.HasOwner(existingBootstrapConfig.GetOwnerReferences(), clusterv1.GroupVersion.String(), []string{"Machine"}) { + ownerReference = &metav1.OwnerReference{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "MachineSet", + Name: ms.Name, + UID: ms.UID, + } + } + + apiVersion, err := contract.GetAPIVersion(ctx, r.Client, ms.Spec.Template.Spec.Bootstrap.ConfigRef.GroupKind()) + if err != nil { + return nil, errors.Wrap(err, "failed to compute desired BootstrapConfig") + } + templateRef := &corev1.ObjectReference{ + APIVersion: apiVersion, + Kind: ms.Spec.Template.Spec.Bootstrap.ConfigRef.Kind, + Namespace: ms.Namespace, + Name: ms.Spec.Template.Spec.Bootstrap.ConfigRef.Name, + } + + template, err := external.Get(ctx, r.Client, templateRef) + if err != nil { + return nil, errors.Wrap(err, "failed to compute desired BootstrapConfig") + } + generateTemplateInput := &external.GenerateTemplateInput{ + Template: template, + TemplateRef: templateRef, + Namespace: machine.Namespace, + Name: machine.Name, + ClusterName: machine.Spec.ClusterName, + OwnerRef: ownerReference, + Labels: machine.Labels, + Annotations: machine.Annotations, + } + bootstrapConfig, err := external.GenerateTemplate(generateTemplateInput) + if err != nil { + return nil, errors.Wrap(err, "failed to compute desired BootstrapConfig") + } + + if existingBootstrapConfig != nil { + bootstrapConfig.SetName(existingBootstrapConfig.GetName()) + bootstrapConfig.SetUID(existingBootstrapConfig.GetUID()) + } + return bootstrapConfig, nil +} + +func (r *Reconciler) createInfraMachine(ctx context.Context, ms *clusterv1.MachineSet, machine *clusterv1.Machine) (*unstructured.Unstructured, clusterv1.ContractVersionedObjectReference, error) { + infraMachine, err := r.computeDesiredInfraMachine(ctx, ms, machine, nil) + if err != nil { + return nil, clusterv1.ContractVersionedObjectReference{}, errors.Wrapf(err, "failed to create InfraMachine") + } + + // Create the full object with capi-machineset. + // Below ssa.RemoveManagedFieldsForLabelsAndAnnotations will drop ownership for labels and annotations + // so that in a subsequent syncMachines call capi-machineset-metadata can take ownership for them. + // Note: This is done in way that it does not rely on managedFields being stored in the cache, so we can optimize + // memory usage by dropping managedFields before storing objects in the cache. + if err := ssa.Patch(ctx, r.Client, machineSetManagerName, infraMachine); err != nil { + return nil, clusterv1.ContractVersionedObjectReference{}, errors.Wrapf(err, "failed to create InfraMachine") + } + + // Note: This field is only used for unit tests that use fake client because the fake client does not properly set resourceVersion + // on BootstrapConfig/InfraMachine after ssa.Patch and then ssa.RemoveManagedFieldsForLabelsAndAnnotations would fail. + if !r.disableRemoveManagedFieldsForLabelsAndAnnotations { + if err := ssa.RemoveManagedFieldsForLabelsAndAnnotations(ctx, r.Client, r.APIReader, infraMachine, machineSetManagerName); err != nil { + return nil, clusterv1.ContractVersionedObjectReference{}, errors.Wrapf(err, "failed to create InfraMachine") + } + } + + return infraMachine, clusterv1.ContractVersionedObjectReference{ + APIGroup: infraMachine.GroupVersionKind().Group, + Kind: infraMachine.GetKind(), + Name: infraMachine.GetName(), + }, nil +} + +func (r *Reconciler) computeDesiredInfraMachine(ctx context.Context, ms *clusterv1.MachineSet, machine *clusterv1.Machine, existingInfraMachine *unstructured.Unstructured) (*unstructured.Unstructured, error) { + var ownerReference *metav1.OwnerReference + if existingInfraMachine == nil || !util.HasOwner(existingInfraMachine.GetOwnerReferences(), clusterv1.GroupVersion.String(), []string{"Machine"}) { + ownerReference = &metav1.OwnerReference{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "MachineSet", + Name: ms.Name, + UID: ms.UID, + } + } + + apiVersion, err := contract.GetAPIVersion(ctx, r.Client, ms.Spec.Template.Spec.InfrastructureRef.GroupKind()) + if err != nil { + return nil, errors.Wrap(err, "failed to compute desired InfraMachine") + } + templateRef := &corev1.ObjectReference{ + APIVersion: apiVersion, + Kind: ms.Spec.Template.Spec.InfrastructureRef.Kind, + Namespace: ms.Namespace, + Name: ms.Spec.Template.Spec.InfrastructureRef.Name, + } + + template, err := external.Get(ctx, r.Client, templateRef) + if err != nil { + return nil, errors.Wrap(err, "failed to compute desired InfraMachine") + } + generateTemplateInput := &external.GenerateTemplateInput{ + Template: template, + TemplateRef: templateRef, + Namespace: machine.Namespace, + Name: machine.Name, + ClusterName: machine.Spec.ClusterName, + OwnerRef: ownerReference, + Labels: machine.Labels, + Annotations: machine.Annotations, + } + infraMachine, err := external.GenerateTemplate(generateTemplateInput) + if err != nil { + return nil, errors.Wrap(err, "failed to compute desired InfraMachine") + } + + if existingInfraMachine != nil { + infraMachine.SetName(existingInfraMachine.GetName()) + infraMachine.SetUID(existingInfraMachine.GetUID()) + } + return infraMachine, nil +} + // Returns the machines to be remediated in the following order // - Machines with RemediateMachineAnnotation annotation if any, // - Machines failing to come up first because diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_controller_status.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_controller_status.go index 031c94579e..40d8bdf89c 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_controller_status.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_controller_status.go @@ -31,6 +31,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + "sigs.k8s.io/cluster-api/internal/util/inplace" "sigs.k8s.io/cluster-api/util/collections" "sigs.k8s.io/cluster-api/util/conditions" clog "sigs.k8s.io/cluster-api/util/log" @@ -81,6 +82,13 @@ func setReplicas(_ context.Context, ms *clusterv1.MachineSet, machines []*cluste var readyReplicas, availableReplicas, upToDateReplicas int32 for _, machine := range machines { + // If a machine is in-place updating consider it not Ready, not Available and not UpToDate. + // Note: We have to check this here because we don't want to rely on an additional Machine controller + // reconcile to set the conditions to be able to update the replica counters here correctly. + if inplace.IsUpdateInProgress(machine) { + continue + } + if conditions.IsTrue(machine, clusterv1.MachineReadyCondition) { readyReplicas++ } @@ -191,7 +199,13 @@ func setScalingDownCondition(_ context.Context, ms *clusterv1.MachineSet, machin if !ms.DeletionTimestamp.IsZero() { desiredReplicas = 0 } - currentReplicas := int32(len(machines)) + currentReplicas := int32(0) + for _, m := range machines { + if _, ok := m.Annotations[clusterv1.PendingAcknowledgeMoveAnnotation]; ok { + continue + } + currentReplicas++ + } // Scaling down. if currentReplicas > desiredReplicas { diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_deletion_order.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_deletion_order.go index 9569548b50..ce42ff2699 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_deletion_order.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_deletion_order.go @@ -24,6 +24,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + "sigs.k8s.io/cluster-api/internal/util/inplace" "sigs.k8s.io/cluster-api/util/conditions" ) @@ -33,23 +34,34 @@ type ( ) const ( - mustDelete deletePriority = 100.0 - shouldDelete deletePriority = 75.0 - betterDelete deletePriority = 50.0 - couldDelete deletePriority = 20.0 - mustNotDelete deletePriority = 0.0 + mustDelete deletePriority = 100.0 + shouldDeleteFirst deletePriority = 80.0 + shouldDelete deletePriority = 75.0 + betterDelete deletePriority = 50.0 + couldDelete deletePriority = 20.0 + mustNotDelete deletePriority = 0.0 secondsPerTenDays float64 = 864000 ) // maps the creation timestamp onto the 0-100 priority range. func oldestDeletionOrder(machine *clusterv1.Machine) deletePriority { + // Deleting machines must go first, otherwise deletion code will delete more machines while previously deleted machines + // are still deleting. if !machine.DeletionTimestamp.IsZero() { return mustDelete } + // If user expressed the intent to delete a machines, respect it by deleting this machine first when scaling down. if _, ok := machine.Annotations[clusterv1.DeleteMachineAnnotation]; ok { + return shouldDeleteFirst + } + // If there is machine still updating in progress and the MS is scaling down, consider this machine next + // so the system avoids to complete unnecessary in-place updates (drop machines not at the desired state first). + if inplace.IsUpdateInProgress(machine) { return shouldDelete } + // If there are machines not healthy, get rid of them next, because this will unblock the rollout + // while respecting the maxUnhealthy requirement. if !isMachineHealthy(machine) { return betterDelete } @@ -64,12 +76,22 @@ func oldestDeletionOrder(machine *clusterv1.Machine) deletePriority { } func newestDeletionOrder(machine *clusterv1.Machine) deletePriority { + // Deleting machines must go first, otherwise deletion code will delete more machines while previously deleted machines + // are still deleting. if !machine.DeletionTimestamp.IsZero() { return mustDelete } + // If user expressed the intent to delete a machines, respect it by deleting this machine first when scaling down. if _, ok := machine.Annotations[clusterv1.DeleteMachineAnnotation]; ok { + return shouldDeleteFirst + } + // If there is machine still updating in progress and the MS is scaling down, consider this machine next + // so the system avoids to complete unnecessary in-place updates (drop machines not at the desired state first). + if inplace.IsUpdateInProgress(machine) { return shouldDelete } + // If there are machines not healthy, get rid of them next, because this will unblock the rollout + // while respecting the maxUnhealthy requirement. if !isMachineHealthy(machine) { return betterDelete } @@ -77,12 +99,22 @@ func newestDeletionOrder(machine *clusterv1.Machine) deletePriority { } func randomDeletionOrder(machine *clusterv1.Machine) deletePriority { + // Deleting machines must go first, otherwise deletion code will delete more machines while previously deleted machines + // are still deleting. if !machine.DeletionTimestamp.IsZero() { return mustDelete } + // If user expressed the intent to delete a machines, respect it by deleting this machine first when scaling down. if _, ok := machine.Annotations[clusterv1.DeleteMachineAnnotation]; ok { + return shouldDeleteFirst + } + // If there is machine still updating in progress and the MS is scaling down, consider this machine next + // so the system avoids to complete unnecessary in-place updates (drop machines not at the desired state first). + if inplace.IsUpdateInProgress(machine) { return shouldDelete } + // If there are machines not healthy, get rid of them next, because this will unblock the rollout + // while respecting the maxUnhealthy requirement. if !isMachineHealthy(machine) { return betterDelete } @@ -106,10 +138,12 @@ func (m sortableMachines) Less(i, j int) bool { return priorityJ < priorityI // high to low } +func getMachinesToMovePrioritized(filteredMachines []*clusterv1.Machine, fun deletePriorityFunc) []*clusterv1.Machine { + return getMachinesToDeletePrioritized(filteredMachines, len(filteredMachines), fun) +} + func getMachinesToDeletePrioritized(filteredMachines []*clusterv1.Machine, diff int, fun deletePriorityFunc) []*clusterv1.Machine { - if diff >= len(filteredMachines) { - return filteredMachines - } else if diff <= 0 { + if diff <= 0 { return []*clusterv1.Machine{} } @@ -119,6 +153,9 @@ func getMachinesToDeletePrioritized(filteredMachines []*clusterv1.Machine, diff } sort.Sort(sortable) + if diff >= len(filteredMachines) { + return sortable.machines + } return sortable.machines[:diff] } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_preflight.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_preflight.go index f983f32799..6b2fd13bbd 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_preflight.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/machineset/machineset_preflight.go @@ -150,8 +150,19 @@ func (r *Reconciler) controlPlaneStablePreflightCheck(controlPlane *unstructured cpKlogRef := klog.KRef(controlPlane.GetNamespace(), controlPlane.GetName()) if feature.Gates.Enabled(feature.ClusterTopology) { - if cluster.Spec.Topology.IsDefined() && cluster.Spec.Topology.Version != controlPlaneVersion { - return ptr.To(fmt.Sprintf("%s %s has a pending version upgrade to %s (%q preflight check failed)", controlPlane.GetKind(), cpKlogRef, cluster.Spec.Topology.Version, clusterv1.MachineSetPreflightCheckControlPlaneIsStable)), nil + // Block when we expect an upgrade to be propagated to the control plane for topology clusters. + // NOTE: in case the cluster is performing an upgrade, allow creation of machines for the current step. + hasSameVersionOfCurrentUpgradeStep := false + if version, ok := cluster.GetAnnotations()[clusterv1.ClusterTopologyUpgradeStepAnnotation]; ok && version != "" { + hasSameVersionOfCurrentUpgradeStep = version == controlPlaneVersion + } + + if cluster.Spec.Topology.IsDefined() && cluster.Spec.Topology.Version != controlPlaneVersion && !hasSameVersionOfCurrentUpgradeStep { + v := cluster.Spec.Topology.Version + if version, ok := cluster.GetAnnotations()[clusterv1.ClusterTopologyUpgradeStepAnnotation]; ok && version != "" { + v = version + } + return ptr.To(fmt.Sprintf("%s %s has a pending version upgrade to %s (%q preflight check failed)", controlPlane.GetKind(), cpKlogRef, v, clusterv1.MachineSetPreflightCheckControlPlaneIsStable)), nil } } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/cluster_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/cluster_controller.go index 1d99891c53..a1f7d7381f 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/cluster_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/cluster_controller.go @@ -55,10 +55,12 @@ import ( "sigs.k8s.io/cluster-api/exp/topology/scope" "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/hooks" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/internal/util/ssa" "sigs.k8s.io/cluster-api/internal/webhooks" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" + "sigs.k8s.io/cluster-api/util/cache" "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/conversion" "sigs.k8s.io/cluster-api/util/patch" @@ -90,6 +92,8 @@ type Reconciler struct { externalTracker external.ObjectTracker recorder record.EventRecorder + hookCache cache.Cache[cache.HookEntry] + // desiredStateGenerator is used to generate the desired state. desiredStateGenerator desiredstate.Generator @@ -106,13 +110,11 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt } predicateLog := ctrl.LoggerFrom(ctx).WithValues("controller", "topology/cluster") - c, err := ctrl.NewControllerManagedBy(mgr). + c, err := capicontrollerutil.NewControllerManagedBy(mgr, predicateLog). For(&clusterv1.Cluster{}, builder.WithPredicates( // Only reconcile Cluster with topology and with changes relevant for this controller. - predicates.All(mgr.GetScheme(), predicateLog, - predicates.ClusterHasTopology(mgr.GetScheme(), predicateLog), - clusterChangeIsRelevant(mgr.GetScheme(), predicateLog), - ), + predicates.ClusterHasTopology(mgr.GetScheme(), predicateLog), + clusterChangeIsRelevant(mgr.GetScheme(), predicateLog), )). Named("topology/cluster"). WatchesRawSource(r.ClusterCache.GetClusterSource("topology/cluster", func(_ context.Context, o client.Object) []ctrl.Request { @@ -121,26 +123,19 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt Watches( &clusterv1.ClusterClass{}, handler.EnqueueRequestsFromMapFunc(r.clusterClassToCluster), - builder.WithPredicates(predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog)), ). Watches( &clusterv1.MachineDeployment{}, handler.EnqueueRequestsFromMapFunc(r.machineDeploymentToCluster), // Only trigger Cluster reconciliation if the MachineDeployment is topology owned, the resource is changed, and the change is relevant. - builder.WithPredicates(predicates.All(mgr.GetScheme(), predicateLog, - predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog), - predicates.ResourceIsTopologyOwned(mgr.GetScheme(), predicateLog), - machineDeploymentChangeIsRelevant(mgr.GetScheme(), predicateLog), - )), + predicates.ResourceIsTopologyOwned(mgr.GetScheme(), predicateLog), + machineDeploymentChangeIsRelevant(mgr.GetScheme(), predicateLog), ). Watches( &clusterv1.MachinePool{}, handler.EnqueueRequestsFromMapFunc(r.machinePoolToCluster), // Only trigger Cluster reconciliation if the MachinePool is topology owned, the resource is changed. - builder.WithPredicates(predicates.All(mgr.GetScheme(), predicateLog, - predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog), - predicates.ResourceIsTopologyOwned(mgr.GetScheme(), predicateLog), - )), + predicates.ResourceIsTopologyOwned(mgr.GetScheme(), predicateLog), ). WithOptions(options). WithEventFilter(predicates.ResourceHasFilterLabel(mgr.GetScheme(), predicateLog, r.WatchFilterValue)). @@ -156,7 +151,20 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt Scheme: mgr.GetScheme(), PredicateLogger: &predicateLog, } - r.desiredStateGenerator = desiredstate.NewGenerator(r.Client, r.ClusterCache, r.RuntimeClient) + r.hookCache = cache.New[cache.HookEntry](cache.HookCacheDefaultTTL) + r.desiredStateGenerator, err = desiredstate.NewGenerator( + r.Client, + r.ClusterCache, + r.RuntimeClient, + r.hookCache, + // Note: We are using 10m so that we are able to relatively quickly pick up changes to the + // upgrade plan from the extension if necessary. + cache.New[desiredstate.GenerateUpgradePlanCacheEntry](10*time.Minute), + ) + if err != nil { + return errors.Wrap(err, "failed creating desired state generator") + } + r.recorder = mgr.GetEventRecorderFor("topology/cluster-controller") r.ssaCache = ssa.NewCache("topology/cluster") return nil @@ -310,7 +318,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re // In case the object is deleted, the managed topology stops to reconcile; // (the other controllers will take care of deletion). if !cluster.DeletionTimestamp.IsZero() { - return r.reconcileDelete(ctx, cluster) + return r.reconcileDelete(ctx, s) } // Handle normal reconciliation loop. @@ -377,6 +385,14 @@ func (r *Reconciler) reconcile(ctx context.Context, s *scope.Scope) (ctrl.Result return ctrl.Result{}, errors.Wrap(err, "error creating dynamic watch") } + anyManagedFieldIssueMitigated, err := r.mitigateManagedFieldsIssue(ctx, s) + if err != nil { + return ctrl.Result{}, err + } + if anyManagedFieldIssueMitigated { + return ctrl.Result{RequeueAfter: 1 * time.Second}, nil // Explicitly requeue as we are not watching all objects. + } + // Computes the desired state of the Cluster and store it in the request scope. s.Desired, err = r.desiredStateGenerator.Generate(ctx, s) if err != nil { @@ -404,10 +420,9 @@ func (r *Reconciler) setupDynamicWatches(ctx context.Context, s *scope.Scope) er if err := r.externalTracker.Watch(ctrl.LoggerFrom(ctx), s.Current.InfrastructureCluster, handler.EnqueueRequestForOwner(scheme, r.Client.RESTMapper(), &clusterv1.Cluster{}), // Only trigger Cluster reconciliation if the InfrastructureCluster is topology owned. - predicates.All(scheme, *r.externalTracker.PredicateLogger, - predicates.ResourceIsChanged(scheme, *r.externalTracker.PredicateLogger), - predicates.ResourceIsTopologyOwned(scheme, *r.externalTracker.PredicateLogger), - )); err != nil { + predicates.ResourceIsChanged(scheme, *r.externalTracker.PredicateLogger), + predicates.ResourceIsTopologyOwned(scheme, *r.externalTracker.PredicateLogger), + ); err != nil { return errors.Wrap(err, "error watching Infrastructure CR") } } @@ -415,10 +430,9 @@ func (r *Reconciler) setupDynamicWatches(ctx context.Context, s *scope.Scope) er if err := r.externalTracker.Watch(ctrl.LoggerFrom(ctx), s.Current.ControlPlane.Object, handler.EnqueueRequestForOwner(scheme, r.Client.RESTMapper(), &clusterv1.Cluster{}), // Only trigger Cluster reconciliation if the ControlPlane is topology owned. - predicates.All(scheme, *r.externalTracker.PredicateLogger, - predicates.ResourceIsChanged(scheme, *r.externalTracker.PredicateLogger), - predicates.ResourceIsTopologyOwned(scheme, *r.externalTracker.PredicateLogger), - )); err != nil { + predicates.ResourceIsChanged(scheme, *r.externalTracker.PredicateLogger), + predicates.ResourceIsTopologyOwned(scheme, *r.externalTracker.PredicateLogger), + ); err != nil { return errors.Wrap(err, "error watching ControlPlane CR") } } @@ -431,8 +445,25 @@ func (r *Reconciler) callBeforeClusterCreateHook(ctx context.Context, s *scope.S log := ctrl.LoggerFrom(ctx) if !s.Current.Cluster.Spec.InfrastructureRef.IsDefined() && !s.Current.Cluster.Spec.ControlPlaneRef.IsDefined() { - v1beta1Cluster := &clusterv1beta1.Cluster{} + // Return quickly if the hook is not defined. + extensionHandlers, err := r.RuntimeClient.GetAllExtensions(ctx, runtimehooksv1.BeforeClusterCreate, s.Current.Cluster) + if err != nil { + return ctrl.Result{}, err + } + if len(extensionHandlers) == 0 { + return ctrl.Result{}, nil + } + + if cacheEntry, ok := r.hookCache.Has(cache.NewHookEntryKey(s.Current.Cluster, runtimehooksv1.BeforeClusterCreate)); ok { + if requeueAfter, requeue := cacheEntry.ShouldRequeue(time.Now()); requeue { + log.V(5).Info(fmt.Sprintf("Skip calling BeforeClusterCreate hook, retry after %s", requeueAfter)) + s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterCreate, cacheEntry.ToResponse(&runtimehooksv1.BeforeClusterCreateResponse{}, requeueAfter)) + return ctrl.Result{RequeueAfter: requeueAfter}, nil + } + } + // DeepCopy cluster because ConvertFrom has side effects like adding the conversion annotation. + v1beta1Cluster := &clusterv1beta1.Cluster{} if err := v1beta1Cluster.ConvertFrom(s.Current.Cluster.DeepCopy()); err != nil { return ctrl.Result{}, errors.Wrap(err, "error converting Cluster to v1beta1 Cluster") } @@ -445,10 +476,14 @@ func (r *Reconciler) callBeforeClusterCreateHook(ctx context.Context, s *scope.S return ctrl.Result{}, err } s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterCreate, hookResponse) + if hookResponse.RetryAfterSeconds != 0 { - log.Info(fmt.Sprintf("Creation of Cluster topology is blocked by %s hook", runtimecatalog.HookName(runtimehooksv1.BeforeClusterCreate))) + r.hookCache.Add(cache.NewHookEntry(s.Current.Cluster, runtimehooksv1.BeforeClusterCreate, time.Now().Add(time.Duration(hookResponse.RetryAfterSeconds)*time.Second), hookResponse.GetMessage())) + log.Info(fmt.Sprintf("Creation of Cluster topology is blocked by %s hook, retry after %ds", runtimecatalog.HookName(runtimehooksv1.BeforeClusterCreate), hookResponse.RetryAfterSeconds)) return ctrl.Result{RequeueAfter: time.Duration(hookResponse.RetryAfterSeconds) * time.Second}, nil } + + log.Info(fmt.Sprintf("Creation of Cluster topology unblocked by %s hook", runtimecatalog.HookName(runtimehooksv1.BeforeClusterCreate))) } return ctrl.Result{}, nil } @@ -519,12 +554,34 @@ func (r *Reconciler) machinePoolToCluster(_ context.Context, o client.Object) [] }} } -func (r *Reconciler) reconcileDelete(ctx context.Context, cluster *clusterv1.Cluster) (ctrl.Result, error) { +func (r *Reconciler) reconcileDelete(ctx context.Context, s *scope.Scope) (ctrl.Result, error) { + cluster := s.Current.Cluster + // Call the BeforeClusterDelete hook if the 'ok-to-delete' annotation is not set // and add the annotation to the cluster after receiving a successful non-blocking response. log := ctrl.LoggerFrom(ctx) if feature.Gates.Enabled(feature.RuntimeSDK) { if !hooks.IsOkToDelete(cluster) { + // Return quickly if the hook is not defined. + extensionHandlers, err := r.RuntimeClient.GetAllExtensions(ctx, runtimehooksv1.BeforeClusterDelete, s.Current.Cluster) + if err != nil { + return ctrl.Result{}, err + } + if len(extensionHandlers) == 0 { + if err := hooks.MarkAsOkToDelete(ctx, r.Client, cluster, false); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + + if cacheEntry, ok := r.hookCache.Has(cache.NewHookEntryKey(s.Current.Cluster, runtimehooksv1.BeforeClusterDelete)); ok { + if requeueAfter, requeue := cacheEntry.ShouldRequeue(time.Now()); requeue { + log.V(5).Info(fmt.Sprintf("Skip calling BeforeClusterDelete hook, retry after %s", requeueAfter)) + s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterDelete, cacheEntry.ToResponse(&runtimehooksv1.BeforeClusterDeleteResponse{}, requeueAfter)) + return ctrl.Result{RequeueAfter: requeueAfter}, nil + } + } + v1beta1Cluster := &clusterv1beta1.Cluster{} // DeepCopy cluster because ConvertFrom has side effects like adding the conversion annotation. if err := v1beta1Cluster.ConvertFrom(cluster.DeepCopy()); err != nil { @@ -538,15 +595,20 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, cluster *clusterv1.Clu if err := r.RuntimeClient.CallAllExtensions(ctx, runtimehooksv1.BeforeClusterDelete, cluster, hookRequest, hookResponse); err != nil { return ctrl.Result{}, err } + // Add the response to the tracker so we can later update condition or requeue when required. + s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterDelete, hookResponse) + if hookResponse.RetryAfterSeconds != 0 { - log.Info(fmt.Sprintf("Cluster deletion is blocked by %q hook", runtimecatalog.HookName(runtimehooksv1.BeforeClusterDelete))) + r.hookCache.Add(cache.NewHookEntry(s.Current.Cluster, runtimehooksv1.BeforeClusterDelete, time.Now().Add(time.Duration(hookResponse.RetryAfterSeconds)*time.Second), hookResponse.GetMessage())) + log.Info(fmt.Sprintf("Cluster deletion is blocked by %q hook, retry after %ds", runtimecatalog.HookName(runtimehooksv1.BeforeClusterDelete), hookResponse.RetryAfterSeconds)) return ctrl.Result{RequeueAfter: time.Duration(hookResponse.RetryAfterSeconds) * time.Second}, nil } // The BeforeClusterDelete hook returned a non-blocking response. Now the cluster is ready to be deleted. // Lets mark the cluster as `ok-to-delete` - if err := hooks.MarkAsOkToDelete(ctx, r.Client, cluster); err != nil { + if err := hooks.MarkAsOkToDelete(ctx, r.Client, cluster, false); err != nil { return ctrl.Result{}, err } + log.Info(fmt.Sprintf("Cluster deletion is unblocked by %s hook", runtimecatalog.HookName(runtimehooksv1.BeforeClusterDelete))) } } return ctrl.Result{}, nil diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/conditions.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/conditions.go index 25858b4336..7cce9a0c3a 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/conditions.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/conditions.go @@ -18,15 +18,19 @@ package cluster import ( "fmt" + "sort" "strings" "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/utils/ptr" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" "sigs.k8s.io/cluster-api/exp/topology/scope" "sigs.k8s.io/cluster-api/internal/contract" + "sigs.k8s.io/cluster-api/internal/hooks" "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/conditions" v1beta1conditions "sigs.k8s.io/cluster-api/util/conditions/deprecated/v1beta1" @@ -74,25 +78,6 @@ func (r *Reconciler) reconcileTopologyReconciledCondition(s *scope.Scope, cluste return nil } - // Mark TopologyReconciled as false due to cluster deletion. - if !cluster.DeletionTimestamp.IsZero() { - v1beta1conditions.Set(cluster, - v1beta1conditions.FalseCondition( - clusterv1.TopologyReconciledV1Beta1Condition, - clusterv1.DeletedV1Beta1Reason, - clusterv1.ConditionSeverityInfo, - "", - ), - ) - conditions.Set(cluster, metav1.Condition{ - Type: clusterv1.ClusterTopologyReconciledCondition, - Status: metav1.ConditionFalse, - Reason: clusterv1.ClusterTopologyReconciledDeletingReason, - Message: "Cluster is deleting", - }) - return nil - } - // If an error occurred during reconciliation set the TopologyReconciled condition to false. // Add the error message from the reconcile function to the message of the condition. if reconcileErr != nil { @@ -115,6 +100,29 @@ func (r *Reconciler) reconcileTopologyReconciledCondition(s *scope.Scope, cluste return nil } + // Mark TopologyReconciled as false due to cluster deletion. + if !cluster.DeletionTimestamp.IsZero() { + message := "Cluster is deleting" + if s.HookResponseTracker.AggregateRetryAfter() != 0 { + message += ". " + s.HookResponseTracker.AggregateMessage("delete") + } + v1beta1conditions.Set(cluster, + v1beta1conditions.FalseCondition( + clusterv1.TopologyReconciledV1Beta1Condition, + clusterv1.DeletingV1Beta1Reason, + clusterv1.ConditionSeverityInfo, + "%s", message, + ), + ) + conditions.Set(cluster, metav1.Condition{ + Type: clusterv1.ClusterTopologyReconciledCondition, + Status: metav1.ConditionFalse, + Reason: clusterv1.ClusterTopologyReconciledDeletingReason, + Message: message, + }) + return nil + } + // If the ClusterClass `metadata.Generation` doesn't match the `status.ObservedGeneration` requeue as the ClusterClass // is not up to date. if s.Blueprint != nil && s.Blueprint.ClusterClass != nil && @@ -138,122 +146,139 @@ func (r *Reconciler) reconcileTopologyReconciledCondition(s *scope.Scope, cluste return nil } - // If any of the lifecycle hooks are blocking any part of the reconciliation then topology - // is not considered as fully reconciled. - if s.HookResponseTracker.AggregateRetryAfter() != 0 { + // If the BeforeClusterCreate hook is blocking, reports it + if !s.Current.Cluster.Spec.InfrastructureRef.IsDefined() && !s.Current.Cluster.Spec.ControlPlaneRef.IsDefined() { + if s.HookResponseTracker.AggregateRetryAfter() != 0 { + v1beta1conditions.Set(cluster, + v1beta1conditions.FalseCondition( + clusterv1.TopologyReconciledV1Beta1Condition, + clusterv1.TopologyReconciledClusterCreatingV1Beta1Reason, + clusterv1.ConditionSeverityInfo, + "%s", s.HookResponseTracker.AggregateMessage("Cluster topology creation"), + ), + ) + conditions.Set(cluster, metav1.Condition{ + Type: clusterv1.ClusterTopologyReconciledCondition, + Status: metav1.ConditionFalse, + Reason: clusterv1.ClusterTopologyReconciledClusterCreatingReason, + Message: s.HookResponseTracker.AggregateMessage("Cluster topology creation"), + }) + return nil + } + + // Note: this should never happen, controlPlane and infrastructure ref should be set at the first reconcile of the topology controller if the hook is not blocking. v1beta1conditions.Set(cluster, - v1beta1conditions.FalseCondition( - clusterv1.TopologyReconciledV1Beta1Condition, - clusterv1.TopologyReconciledHookBlockingV1Beta1Reason, - clusterv1.ConditionSeverityInfo, - // TODO: Add a protection for messages continuously changing leading to Cluster object changes/reconcile. - "%s", s.HookResponseTracker.AggregateMessage(), - ), + v1beta1conditions.TrueCondition(clusterv1.TopologyReconciledV1Beta1Condition), ) conditions.Set(cluster, metav1.Condition{ Type: clusterv1.ClusterTopologyReconciledCondition, - Status: metav1.ConditionFalse, - Reason: clusterv1.ClusterTopologyReconciledHookBlockingReason, - // TODO: Add a protection for messages continuously changing leading to Cluster object changes/reconcile. - Message: s.HookResponseTracker.AggregateMessage(), + Status: metav1.ConditionTrue, + Reason: clusterv1.ClusterTopologyReconcileSucceededReason, }) return nil } - // The topology is not considered as fully reconciled if one of the following is true: - // * either the Control Plane or any of the MachineDeployments/MachinePools are still pending to pick up the new version - // (generally happens when upgrading the cluster) - // * when there are MachineDeployments/MachinePools for which the upgrade has been deferred - // * when new MachineDeployments/MachinePools are pending to be created - // (generally happens when upgrading the cluster) + // If Cluster is updating surface it. + // Note: intentionally checking all the signal about upgrade in progress to make sure to avoid edge cases. if s.UpgradeTracker.ControlPlane.IsPendingUpgrade || - s.UpgradeTracker.MachineDeployments.IsAnyPendingCreate() || + s.UpgradeTracker.ControlPlane.IsStartingUpgrade || + s.UpgradeTracker.ControlPlane.IsUpgrading || s.UpgradeTracker.MachineDeployments.IsAnyPendingUpgrade() || - s.UpgradeTracker.MachineDeployments.DeferredUpgrade() || - s.UpgradeTracker.MachinePools.IsAnyPendingCreate() || + s.UpgradeTracker.MachineDeployments.IsAnyUpgrading() || + s.UpgradeTracker.MachineDeployments.IsAnyUpgradeDeferred() || + s.UpgradeTracker.MachineDeployments.IsAnyPendingCreate() || s.UpgradeTracker.MachinePools.IsAnyPendingUpgrade() || - s.UpgradeTracker.MachinePools.DeferredUpgrade() { + s.UpgradeTracker.MachinePools.IsAnyUpgrading() || + s.UpgradeTracker.MachinePools.IsAnyUpgradeDeferred() || + s.UpgradeTracker.MachinePools.IsAnyPendingCreate() || + hooks.IsPending(runtimehooksv1.AfterClusterUpgrade, cluster) { + // Start building the condition message showing upgrade progress. msgBuilder := &strings.Builder{} - var reason string - var v1beta2Reason string - - // TODO(ykakarap): Evaluate potential improvements to building the condition. Multiple causes can trigger the - // condition to be false at the same time (Example: ControlPlane.IsPendingUpgrade and MachineDeployments.IsAnyPendingCreate can - // occur at the same time). Find better wording and `Reason` for the condition so that the condition can be rich - // with all the relevant information. - switch { - case s.UpgradeTracker.ControlPlane.IsPendingUpgrade: - fmt.Fprintf(msgBuilder, "Control plane rollout and upgrade to version %s on hold.", s.Blueprint.Topology.Version) - reason = clusterv1.TopologyReconciledControlPlaneUpgradePendingV1Beta1Reason - v1beta2Reason = clusterv1.ClusterTopologyReconciledControlPlaneUpgradePendingReason - case s.UpgradeTracker.MachineDeployments.IsAnyPendingUpgrade(): - fmt.Fprintf(msgBuilder, "MachineDeployment(s) %s rollout and upgrade to version %s on hold.", - computeNameList(s.UpgradeTracker.MachineDeployments.PendingUpgradeNames()), - s.Blueprint.Topology.Version, - ) - reason = clusterv1.TopologyReconciledMachineDeploymentsUpgradePendingV1Beta1Reason - v1beta2Reason = clusterv1.ClusterTopologyReconciledMachineDeploymentsUpgradePendingReason - case s.UpgradeTracker.MachineDeployments.IsAnyPendingCreate(): - fmt.Fprintf(msgBuilder, "MachineDeployment(s) for Topologies %s creation on hold.", - computeNameList(s.UpgradeTracker.MachineDeployments.PendingCreateTopologyNames()), - ) - reason = clusterv1.TopologyReconciledMachineDeploymentsCreatePendingV1Beta1Reason - v1beta2Reason = clusterv1.ClusterTopologyReconciledMachineDeploymentsCreatePendingReason - case s.UpgradeTracker.MachineDeployments.DeferredUpgrade(): - fmt.Fprintf(msgBuilder, "MachineDeployment(s) %s rollout and upgrade to version %s deferred.", - computeNameList(s.UpgradeTracker.MachineDeployments.DeferredUpgradeNames()), - s.Blueprint.Topology.Version, - ) - reason = clusterv1.TopologyReconciledMachineDeploymentsUpgradeDeferredV1Beta1Reason - v1beta2Reason = clusterv1.ClusterTopologyReconciledMachineDeploymentsUpgradeDeferredReason - case s.UpgradeTracker.MachinePools.IsAnyPendingUpgrade(): - fmt.Fprintf(msgBuilder, "MachinePool(s) %s rollout and upgrade to version %s on hold.", - computeNameList(s.UpgradeTracker.MachinePools.PendingUpgradeNames()), - s.Blueprint.Topology.Version, - ) - reason = clusterv1.TopologyReconciledMachinePoolsUpgradePendingV1Beta1Reason - v1beta2Reason = clusterv1.ClusterTopologyReconciledMachinePoolsUpgradePendingReason - case s.UpgradeTracker.MachinePools.IsAnyPendingCreate(): - fmt.Fprintf(msgBuilder, "MachinePool(s) for Topologies %s creation on hold.", - computeNameList(s.UpgradeTracker.MachinePools.PendingCreateTopologyNames()), - ) - reason = clusterv1.TopologyReconciledMachinePoolsCreatePendingV1Beta1Reason - v1beta2Reason = clusterv1.ClusterTopologyReconciledMachinePoolsCreatePendingReason - case s.UpgradeTracker.MachinePools.DeferredUpgrade(): - fmt.Fprintf(msgBuilder, "MachinePool(s) %s rollout and upgrade to version %s deferred.", - computeNameList(s.UpgradeTracker.MachinePools.DeferredUpgradeNames()), - s.Blueprint.Topology.Version, - ) - reason = clusterv1.TopologyReconciledMachinePoolsUpgradeDeferredV1Beta1Reason - v1beta2Reason = clusterv1.ClusterTopologyReconciledMachinePoolsUpgradeDeferredReason + fmt.Fprintf(msgBuilder, "Cluster is upgrading to %s", cluster.Spec.Topology.Version) + + // Setting condition reasons showing upgrade is progress; this will be overridden only + // when users are blocking upgrade to make further progress, e.g. deferred upgrades + reason := clusterv1.ClusterTopologyReconciledClusterUpgradingReason + v1Beta1Reason := clusterv1.TopologyReconciledClusterUpgradingV1Beta1Reason + + cpVersion, err := contract.ControlPlane().Version().Get(s.Desired.ControlPlane.Object) + if err != nil { + return errors.Wrap(err, "failed to get control plane spec version") + } + + // If any of the lifecycle hooks are blocking the upgrade surface it as a first detail. + if s.HookResponseTracker.IsAnyBlocking() { + fmt.Fprintf(msgBuilder, "\n * %s", s.HookResponseTracker.AggregateMessage("upgrade")) + } + + // If control plane is upgrading surface it, otherwise surface the pending upgrade plan. + if s.UpgradeTracker.ControlPlane.IsStartingUpgrade || s.UpgradeTracker.ControlPlane.IsUpgrading { + fmt.Fprintf(msgBuilder, "\n * %s upgrading to version %s%s", s.Current.ControlPlane.Object.GetKind(), *cpVersion, pendingVersions(s.UpgradeTracker.ControlPlane.UpgradePlan, *cpVersion)) + } else if len(s.UpgradeTracker.ControlPlane.UpgradePlan) > 0 { + fmt.Fprintf(msgBuilder, "\n * %s pending upgrade to version %s", s.Current.ControlPlane.Object.GetKind(), strings.Join(s.UpgradeTracker.ControlPlane.UpgradePlan, ", ")) } - switch { - case s.UpgradeTracker.ControlPlane.IsProvisioning: - msgBuilder.WriteString(" Control plane is completing initial provisioning") + // If MachineDeployments are upgrading surface it, if MachineDeployments are pending upgrades then surface the upgrade plans. + upgradingMachineDeploymentNames, pendingMachineDeploymentNames, deferredMachineDeploymentNames := dedupNames(s.UpgradeTracker.MachineDeployments) + if len(upgradingMachineDeploymentNames) > 0 { + fmt.Fprintf(msgBuilder, "\n * %s upgrading to version %s%s", nameList("MachineDeployment", "MachineDeployments", upgradingMachineDeploymentNames), *cpVersion, pendingVersions(s.UpgradeTracker.MachineDeployments.UpgradePlan, *cpVersion)) + } - case s.UpgradeTracker.ControlPlane.IsUpgrading: - cpVersion, err := contract.ControlPlane().Version().Get(s.Current.ControlPlane.Object) - if err != nil { - return errors.Wrap(err, "failed to get control plane spec version") + if len(pendingMachineDeploymentNames) > 0 && len(s.UpgradeTracker.MachineDeployments.UpgradePlan) > 0 { + fmt.Fprintf(msgBuilder, "\n * %s pending upgrade to version %s", nameList("MachineDeployment", "MachineDeployments", pendingMachineDeploymentNames), strings.Join(s.UpgradeTracker.MachineDeployments.UpgradePlan, ", ")) + } + + // If MachineDeployments has been deferred or put on hold, surface it. + if len(deferredMachineDeploymentNames) > 0 { + fmt.Fprintf(msgBuilder, "\n * %s upgrade to version %s deferred using defer-upgrade or hold-upgrade-sequence annotations", nameList("MachineDeployment", "MachineDeployments", deferredMachineDeploymentNames), *cpVersion) + // If Deferred upgrades are blocking an upgrade, surface it. + // Note: Hook blocking takes the precedence on this signal. + if !s.HookResponseTracker.IsAnyBlocking() && + (!s.UpgradeTracker.ControlPlane.IsStartingUpgrade && !s.UpgradeTracker.ControlPlane.IsUpgrading) && + !s.UpgradeTracker.MachineDeployments.IsAnyUpgrading() && len(pendingMachineDeploymentNames) == 0 { + reason = clusterv1.ClusterTopologyReconciledMachineDeploymentsUpgradeDeferredReason + v1Beta1Reason = clusterv1.TopologyReconciledMachineDeploymentsUpgradeDeferredV1Beta1Reason } - fmt.Fprintf(msgBuilder, " Control plane is upgrading to version %s", *cpVersion) + } - case len(s.UpgradeTracker.MachineDeployments.UpgradingNames()) > 0: - fmt.Fprintf(msgBuilder, " MachineDeployment(s) %s are upgrading", - computeNameList(s.UpgradeTracker.MachineDeployments.UpgradingNames()), - ) + // If creation of MachineDeployments has been deferred due to control plane upgrade in progress, surface it. + if s.UpgradeTracker.MachineDeployments.IsAnyPendingCreate() { + fmt.Fprintf(msgBuilder, "\n * %s creation deferred while control plane upgrade is in progress", nameList("MachineDeployment", "MachineDeployments", s.UpgradeTracker.MachineDeployments.PendingCreateTopologyNames())) + } - case len(s.UpgradeTracker.MachinePools.UpgradingNames()) > 0: - fmt.Fprintf(msgBuilder, " MachinePool(s) %s are upgrading", - computeNameList(s.UpgradeTracker.MachinePools.UpgradingNames()), - ) + // If MachinePools are upgrading surface it, if MachinePools are pending upgrades then surface the upgrade plans. + upgradingMachinePoolNames, pendingMachinePoolNames, deferredMachinePoolNames := dedupNames(s.UpgradeTracker.MachinePools) + if len(upgradingMachinePoolNames) > 0 { + fmt.Fprintf(msgBuilder, "\n * %s upgrading to version %s%s", nameList("MachinePool", "MachinePools", upgradingMachinePoolNames), *cpVersion, pendingVersions(s.UpgradeTracker.MachinePools.UpgradePlan, *cpVersion)) + } + + if len(pendingMachinePoolNames) > 0 && len(s.UpgradeTracker.MachinePools.UpgradePlan) > 0 { + fmt.Fprintf(msgBuilder, "\n * %s pending upgrade to version %s", nameList("MachinePool", "MachinePools", pendingMachinePoolNames), strings.Join(s.UpgradeTracker.MachinePools.UpgradePlan, ", ")) + } + + // If MachinePools has been deferred or put on hold, surface it. + if len(deferredMachinePoolNames) > 0 { + fmt.Fprintf(msgBuilder, "\n * %s upgrade to version %s deferred using topology.cluster.x-k8s.io/defer-upgrade or hold-upgrade-sequence annotations", nameList("MachinePool", "MachinePools", deferredMachinePoolNames), *cpVersion) + // If Deferred upgrades are blocking an upgrade, surface it. + // Note: Hook blocking takes the precedence on this signal. + if !s.HookResponseTracker.IsAnyBlocking() && + (!s.UpgradeTracker.ControlPlane.IsStartingUpgrade && !s.UpgradeTracker.ControlPlane.IsUpgrading) && + !s.UpgradeTracker.MachinePools.IsAnyUpgrading() && len(pendingMachinePoolNames) == 0 && + reason != clusterv1.ClusterTopologyReconciledMachineDeploymentsUpgradeDeferredReason { + reason = clusterv1.ClusterTopologyReconciledMachinePoolsUpgradeDeferredReason + v1Beta1Reason = clusterv1.TopologyReconciledMachinePoolsUpgradeDeferredV1Beta1Reason + } + } + + // If creation of MachinePools has been deferred due to control plane upgrade in progress, surface it. + if s.UpgradeTracker.MachinePools.IsAnyPendingCreate() { + fmt.Fprintf(msgBuilder, "\n * %s creation deferred while control plane upgrade is in progress", nameList("MachinePool", "MachinePools", s.UpgradeTracker.MachinePools.PendingCreateTopologyNames())) } v1beta1conditions.Set(cluster, v1beta1conditions.FalseCondition( clusterv1.TopologyReconciledV1Beta1Condition, - reason, + v1Beta1Reason, clusterv1.ConditionSeverityInfo, "%s", msgBuilder.String(), ), @@ -261,7 +286,7 @@ func (r *Reconciler) reconcileTopologyReconciledCondition(s *scope.Scope, cluste conditions.Set(cluster, metav1.Condition{ Type: clusterv1.ClusterTopologyReconciledCondition, Status: metav1.ConditionFalse, - Reason: v1beta2Reason, + Reason: reason, Message: msgBuilder.String(), }) return nil @@ -281,13 +306,54 @@ func (r *Reconciler) reconcileTopologyReconciledCondition(s *scope.Scope, cluste return nil } +// pendingVersion return a message with pending version in the upgrad plan. +func pendingVersions(plan []string, version string) string { + // clone the plan to avoid side effects on the original object. + planWithoutVersion := []string{} + for _, v := range plan { + if v != version { + planWithoutVersion = append(planWithoutVersion, v) + } + } + if len(planWithoutVersion) > 0 { + return fmt.Sprintf(" (%s pending)", strings.Join(planWithoutVersion, ", ")) + } + return "" +} + +// dedupNames take care of names that might exist in multiple lists. +func dedupNames(t scope.WorkerUpgradeTracker) ([]string, []string, []string) { + // upgrading names are preserved + upgradingSet := sets.Set[string]{}.Insert(t.UpgradingNames()...) + // upgrading names are removed from deferred names (give precedence to the fact that it is upgrading now) + deferredSet := sets.Set[string]{}.Insert(t.DeferredUpgradeNames()...).Difference(upgradingSet) + // upgrading and deferred names are removed from pending names (it is pending if not upgrading or deferred) + pendingSet := sets.Set[string]{}.Insert(t.PendingUpgradeNames()...).Difference(upgradingSet).Difference(deferredSet) + return upgradingSet.UnsortedList(), pendingSet.UnsortedList(), deferredSet.UnsortedList() +} + // computeNameList computes list of names from the given list to be shown in conditions. // It shortens the list to at most 5 names and adds an ellipsis at the end if the list -// has more than 5 elements. +// has more than 3 elements. func computeNameList(list []string) any { - if len(list) > 5 { - list = append(list[:5], "...") + if len(list) > 3 { + list = append(list[:3], "...") } return strings.Join(list, ", ") } + +// nameList computes list of names from the given list to be shown in conditions. +// It shortens the list to at most 5 names and adds an ellipsis at the end if the list +// has more than 3 elements. +func nameList(kind, kindPlural string, names []string) any { + sort.Strings(names) + switch { + case len(names) == 1: + return fmt.Sprintf("%s %s", kind, names[0]) + case len(names) <= 3: + return fmt.Sprintf("%s %s", kindPlural, strings.Join(names, ", ")) + default: + return fmt.Sprintf("%s %s, ... (%d more)", kindPlural, strings.Join(names[:3], ", "), len(names)-3) + } +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/managedfieldsmitigation.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/managedfieldsmitigation.go new file mode 100644 index 0000000000..f55af266e7 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/managedfieldsmitigation.go @@ -0,0 +1,110 @@ +/* +Copyright 2026 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 cluster + +import ( + "context" + + "sigs.k8s.io/cluster-api/exp/topology/scope" + "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge" + "sigs.k8s.io/cluster-api/internal/util/ssa" +) + +func (r *Reconciler) mitigateManagedFieldsIssue(ctx context.Context, s *scope.Scope) (bool, error) { + if s.Current == nil { + return false, nil + } + + anyManagedFieldIssueMitigated, err := ssa.MitigateManagedFieldsIssue(ctx, r.Client, s.Current.Cluster, structuredmerge.TopologyManagerName) + if err != nil { + return false, err + } + + managedFieldIssueMitigated, err := ssa.MitigateManagedFieldsIssue(ctx, r.Client, s.Current.InfrastructureCluster, structuredmerge.TopologyManagerName) + if err != nil { + return false, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + + if s.Current.ControlPlane != nil { + managedFieldIssueMitigated, err := ssa.MitigateManagedFieldsIssue(ctx, r.Client, s.Current.ControlPlane.MachineHealthCheck, structuredmerge.TopologyManagerName) + if err != nil { + return false, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + + managedFieldIssueMitigated, err = ssa.MitigateManagedFieldsIssue(ctx, r.Client, s.Current.ControlPlane.Object, structuredmerge.TopologyManagerName) + if err != nil { + return false, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + + managedFieldIssueMitigated, err = ssa.MitigateManagedFieldsIssue(ctx, r.Client, s.Current.ControlPlane.InfrastructureMachineTemplate, structuredmerge.TopologyManagerName) + if err != nil { + return false, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + } + + for _, md := range s.Current.MachineDeployments { + managedFieldIssueMitigated, err := ssa.MitigateManagedFieldsIssue(ctx, r.Client, md.MachineHealthCheck, structuredmerge.TopologyManagerName) + if err != nil { + return false, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + + managedFieldIssueMitigated, err = ssa.MitigateManagedFieldsIssue(ctx, r.Client, md.Object, structuredmerge.TopologyManagerName) + if err != nil { + return false, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + + managedFieldIssueMitigated, err = ssa.MitigateManagedFieldsIssue(ctx, r.Client, md.InfrastructureMachineTemplate, structuredmerge.TopologyManagerName) + if err != nil { + return false, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + + managedFieldIssueMitigated, err = ssa.MitigateManagedFieldsIssue(ctx, r.Client, md.BootstrapTemplate, structuredmerge.TopologyManagerName) + if err != nil { + return false, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + } + + for _, mp := range s.Current.MachinePools { + managedFieldIssueMitigated, err := ssa.MitigateManagedFieldsIssue(ctx, r.Client, mp.Object, structuredmerge.TopologyManagerName) + if err != nil { + return false, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + + managedFieldIssueMitigated, err = ssa.MitigateManagedFieldsIssue(ctx, r.Client, mp.InfrastructureMachinePoolObject, structuredmerge.TopologyManagerName) + if err != nil { + return false, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + + managedFieldIssueMitigated, err = ssa.MitigateManagedFieldsIssue(ctx, r.Client, mp.BootstrapObject, structuredmerge.TopologyManagerName) + if err != nil { + return false, err + } + anyManagedFieldIssueMitigated = anyManagedFieldIssueMitigated || managedFieldIssueMitigated + } + + return anyManagedFieldIssueMitigated, nil +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/engine.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/engine.go index 60f524e6e6..428102b02b 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/engine.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/engine.go @@ -41,6 +41,7 @@ import ( "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/external" "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/inline" "sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/variables" + patchutil "sigs.k8s.io/cluster-api/internal/util/patch" ) // Engine is a patch engine which applies patches defined in a ClusterBlueprint to a ClusterState. @@ -504,7 +505,7 @@ func applyPatchToRequest(ctx context.Context, req *runtimehooksv1.GeneratePatche // Overwrite the spec of template.Template with the spec of the patchedTemplate, // to ensure that we only pick up changes to the spec. - if err := patchTemplateSpec(&requestItem.Object, patchedTemplate); err != nil { + if err := patchutil.Patch(&requestItem.Object, patchedTemplate, "spec"); err != nil { log.Error(err, fmt.Sprintf("Failed to apply patch to template with uid %q", requestItem.UID)) return errors.Wrap(err, "failed to apply patch to template") } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/patch.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/patch.go index 174d453895..06b1bb6dac 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/patch.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches/patch.go @@ -21,16 +21,15 @@ import ( "context" "encoding/json" "fmt" - "strings" jsonpatch "github.com/evanphx/json-patch/v5" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/cluster-api/internal/contract" + patchutil "sigs.k8s.io/cluster-api/internal/util/patch" ) // PatchOption represents an option for the patchObject and patchTemplate funcs. @@ -88,12 +87,12 @@ func patchUnstructured(ctx context.Context, original, modified *unstructured.Uns patched := original.DeepCopy() // copySpec overwrites patched.destSpecPath with modified.srcSpecPath. - if err := copySpec(copySpecInput{ - src: modified, - dest: patched, - srcSpecPath: srcSpecPath, - destSpecPath: destSpecPath, - fieldsToPreserve: patchOptions.preserveFields, + if err := patchutil.CopySpec(patchutil.CopySpecInput{ + Src: modified, + Dest: patched, + SrcSpecPath: srcSpecPath, + DestSpecPath: destSpecPath, + FieldsToPreserve: patchOptions.preserveFields, }); err != nil { return errors.Wrapf(err, "failed to apply patch to %s %s", original.GetKind(), klog.KObj(original)) } @@ -135,82 +134,3 @@ func calculateDiff(original, patched *unstructured.Unstructured) ([]byte, error) } return diff, nil } - -// patchTemplateSpec overwrites spec in templateJSON with spec of patchedTemplateBytes. -func patchTemplateSpec(templateJSON *runtime.RawExtension, patchedTemplateBytes []byte) error { - // Convert templates to Unstructured. - template, err := bytesToUnstructured(templateJSON.Raw) - if err != nil { - return errors.Wrap(err, "failed to convert template to Unstructured") - } - patchedTemplate, err := bytesToUnstructured(patchedTemplateBytes) - if err != nil { - return errors.Wrap(err, "failed to convert patched template to Unstructured") - } - - // Copy spec from patchedTemplate to template. - if err := copySpec(copySpecInput{ - src: patchedTemplate, - dest: template, - srcSpecPath: "spec", - destSpecPath: "spec", - }); err != nil { - return errors.Wrap(err, "failed to apply patch to template") - } - - // Marshal template and store it in templateJSON. - templateBytes, err := template.MarshalJSON() - if err != nil { - return errors.Wrapf(err, "failed to marshal patched template") - } - templateJSON.Object = template - templateJSON.Raw = templateBytes - return nil -} - -type copySpecInput struct { - src *unstructured.Unstructured - dest *unstructured.Unstructured - srcSpecPath string - destSpecPath string - fieldsToPreserve []contract.Path -} - -// copySpec copies a field from a srcSpecPath in src to a destSpecPath in dest, -// while preserving fieldsToPreserve. -func copySpec(in copySpecInput) error { - // Backup fields that should be preserved from dest. - preservedFields := map[string]interface{}{} - for _, field := range in.fieldsToPreserve { - value, found, err := unstructured.NestedFieldNoCopy(in.dest.Object, field...) - if !found { - // Continue if the field does not exist in src. fieldsToPreserve don't have to exist. - continue - } else if err != nil { - return errors.Wrapf(err, "failed to get field %q from %s %s", strings.Join(field, "."), in.dest.GetKind(), klog.KObj(in.dest)) - } - preservedFields[strings.Join(field, ".")] = value - } - - // Get spec from src. - srcSpec, found, err := unstructured.NestedFieldNoCopy(in.src.Object, strings.Split(in.srcSpecPath, ".")...) - if !found { - // Return if srcSpecPath does not exist in src, nothing to do. - return nil - } else if err != nil { - return errors.Wrapf(err, "failed to get field %q from %s %s", in.srcSpecPath, in.src.GetKind(), klog.KObj(in.src)) - } - - // Set spec in dest. - if err := unstructured.SetNestedField(in.dest.Object, srcSpec, strings.Split(in.destSpecPath, ".")...); err != nil { - return errors.Wrapf(err, "failed to set field %q on %s %s", in.destSpecPath, in.dest.GetKind(), klog.KObj(in.dest)) - } - - // Restore preserved fields. - for path, value := range preservedFields { - if err := unstructured.SetNestedField(in.dest.Object, value, strings.Split(path, ".")...); err != nil { - return errors.Wrapf(err, "failed to set field %q on %s %s", path, in.dest.GetKind(), klog.KObj(in.dest)) - } - } - return nil -} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/reconcile_state.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/reconcile_state.go index 83c285b4f4..d7cf5decb7 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/reconcile_state.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/reconcile_state.go @@ -30,7 +30,6 @@ import ( kerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/storage/names" "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" @@ -39,6 +38,7 @@ import ( clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" + runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" "sigs.k8s.io/cluster-api/exp/topology/scope" "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/contract" @@ -48,7 +48,9 @@ import ( "sigs.k8s.io/cluster-api/internal/topology/clustershim" topologynames "sigs.k8s.io/cluster-api/internal/topology/names" "sigs.k8s.io/cluster-api/internal/topology/ownerrefs" + clientutil "sigs.k8s.io/cluster-api/internal/util/client" "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/cache" ) const ( @@ -187,7 +189,7 @@ func (r *Reconciler) callAfterHooks(ctx context.Context, s *scope.Scope) error { func (r *Reconciler) callAfterControlPlaneInitialized(ctx context.Context, s *scope.Scope) error { // If the cluster topology is being created then track to intent to call the AfterControlPlaneInitialized hook so that we can call it later. if !s.Current.Cluster.Spec.InfrastructureRef.IsDefined() && !s.Current.Cluster.Spec.ControlPlaneRef.IsDefined() { - if err := hooks.MarkAsPending(ctx, r.Client, s.Current.Cluster, runtimehooksv1.AfterControlPlaneInitialized); err != nil { + if err := hooks.MarkAsPending(ctx, r.Client, s.Current.Cluster, false, runtimehooksv1.AfterControlPlaneInitialized); err != nil { return err } } @@ -211,7 +213,7 @@ func (r *Reconciler) callAfterControlPlaneInitialized(ctx context.Context, s *sc return err } s.HookResponseTracker.Add(runtimehooksv1.AfterControlPlaneInitialized, hookResponse) - if err := hooks.MarkAsDone(ctx, r.Client, s.Current.Cluster, runtimehooksv1.AfterControlPlaneInitialized); err != nil { + if err := hooks.MarkAsDone(ctx, r.Client, s.Current.Cluster, false, runtimehooksv1.AfterControlPlaneInitialized); err != nil { return err } } @@ -232,9 +234,12 @@ func isControlPlaneInitialized(cluster *clusterv1.Cluster) bool { } func (r *Reconciler) callAfterClusterUpgrade(ctx context.Context, s *scope.Scope) error { + log := ctrl.LoggerFrom(ctx) + // Call the hook only if we are tracking the intent to do so. If it is not tracked it means we don't need to call the // hook because we didn't go through an upgrade or we already called the hook after the upgrade. - if hooks.IsPending(runtimehooksv1.AfterClusterUpgrade, s.Current.Cluster) { + // Note: also check that the AfterControlPlaneUpgrade hooks and the AfterWorkersUpgrade hooks already have been called. + if hooks.IsPending(runtimehooksv1.AfterClusterUpgrade, s.Current.Cluster) && !hooks.IsPending(runtimehooksv1.AfterControlPlaneUpgrade, s.Current.Cluster) && !hooks.IsPending(runtimehooksv1.AfterWorkersUpgrade, s.Current.Cluster) { // Call the registered extensions for the hook after the cluster is fully upgraded. // A clusters is considered fully upgraded if: // - Control plane is stable (not upgrading, not scaling, not about to upgrade) @@ -242,16 +247,33 @@ func (r *Reconciler) callAfterClusterUpgrade(ctx context.Context, s *scope.Scope // - MachineDeployments/MachinePools are not pending an upgrade // - MachineDeployments/MachinePools are not pending create if s.UpgradeTracker.ControlPlane.IsControlPlaneStable() && // Control Plane stable checks - len(s.UpgradeTracker.MachineDeployments.UpgradingNames()) == 0 && // Machine deployments are not upgrading or not about to upgrade + !s.UpgradeTracker.MachineDeployments.IsAnyUpgrading() && // Machine deployments are not upgrading or not about to upgrade !s.UpgradeTracker.MachineDeployments.IsAnyPendingCreate() && // No MachineDeployments are pending create !s.UpgradeTracker.MachineDeployments.IsAnyPendingUpgrade() && // No MachineDeployments are pending an upgrade - !s.UpgradeTracker.MachineDeployments.DeferredUpgrade() && // No MachineDeployments have deferred an upgrade - len(s.UpgradeTracker.MachinePools.UpgradingNames()) == 0 && // Machine pools are not upgrading or not about to upgrade + !s.UpgradeTracker.MachineDeployments.IsAnyUpgradeDeferred() && // No MachineDeployments have deferred an upgrade + !s.UpgradeTracker.MachinePools.IsAnyUpgrading() && // Machine pools are not upgrading or not about to upgrade !s.UpgradeTracker.MachinePools.IsAnyPendingCreate() && // No MachinePools are pending create !s.UpgradeTracker.MachinePools.IsAnyPendingUpgrade() && // No MachinePools are pending an upgrade - !s.UpgradeTracker.MachinePools.DeferredUpgrade() { // No MachinePools have deferred an upgrade - v1beta1Cluster := &clusterv1beta1.Cluster{} + !s.UpgradeTracker.MachinePools.IsAnyUpgradeDeferred() { // No MachinePools have deferred an upgrade + // Return quickly if the hook is not defined. + extensionHandlers, err := r.RuntimeClient.GetAllExtensions(ctx, runtimehooksv1.AfterClusterUpgrade, s.Current.Cluster) + if err != nil { + return err + } + if len(extensionHandlers) == 0 { + return hooks.MarkAsDone(ctx, r.Client, s.Current.Cluster, false, runtimehooksv1.AfterClusterUpgrade) + } + + if cacheEntry, ok := r.hookCache.Has(cache.NewHookEntryKey(s.Current.Cluster, runtimehooksv1.AfterClusterUpgrade)); ok { + if requeueAfter, requeue := cacheEntry.ShouldRequeue(time.Now()); requeue { + log.V(5).Info(fmt.Sprintf("Skip calling AfterClusterUpgrade hook, retry after %s", requeueAfter)) + s.HookResponseTracker.Add(runtimehooksv1.AfterClusterUpgrade, cacheEntry.ToResponse(&runtimehooksv1.AfterClusterUpgradeResponse{}, requeueAfter)) + return nil + } + } + // DeepCopy cluster because ConvertFrom has side effects like adding the conversion annotation. + v1beta1Cluster := &clusterv1beta1.Cluster{} if err := v1beta1Cluster.ConvertFrom(s.Current.Cluster.DeepCopy()); err != nil { return errors.Wrap(err, "error converting Cluster to v1beta1 Cluster") } @@ -266,10 +288,19 @@ func (r *Reconciler) callAfterClusterUpgrade(ctx context.Context, s *scope.Scope return err } s.HookResponseTracker.Add(runtimehooksv1.AfterClusterUpgrade, hookResponse) + + if hookResponse.RetryAfterSeconds != 0 { + r.hookCache.Add(cache.NewHookEntry(s.Current.Cluster, runtimehooksv1.AfterClusterUpgrade, time.Now().Add(time.Duration(hookResponse.RetryAfterSeconds)*time.Second), hookResponse.GetMessage())) + log.Info(fmt.Sprintf("Cluster upgrade to version %s completed but next upgrades are blocked by %s hook, retry after %ds", hookRequest.KubernetesVersion, runtimecatalog.HookName(runtimehooksv1.AfterClusterUpgrade), hookResponse.RetryAfterSeconds)) + return nil + } + // The hook is successfully called; we can remove this hook from the list of pending-hooks. - if err := hooks.MarkAsDone(ctx, r.Client, s.Current.Cluster, runtimehooksv1.AfterClusterUpgrade); err != nil { + if err := hooks.MarkAsDone(ctx, r.Client, s.Current.Cluster, false, runtimehooksv1.AfterClusterUpgrade); err != nil { return err } + + log.Info(fmt.Sprintf("Cluster upgrade to version %s and %s hook completed", hookRequest.KubernetesVersion, runtimecatalog.HookName(runtimehooksv1.AfterClusterUpgrade))) } } @@ -384,11 +415,17 @@ func (r *Reconciler) reconcileControlPlane(ctx context.Context, s *scope.Scope) // Create or update the ControlPlaneObject for the ControlPlaneState. log := ctrl.LoggerFrom(ctx).WithValues(s.Desired.ControlPlane.Object.GetKind(), klog.KObj(s.Desired.ControlPlane.Object)) ctx = ctrl.LoggerInto(ctx, log) + + ignorePaths, err := contract.ControlPlane().IgnorePaths(s.Desired.ControlPlane.Object) + if err != nil { + return false, errors.Wrap(err, "failed to calculate ignore paths") + } created, err := r.reconcileReferencedObject(ctx, reconcileReferencedObjectInput{ cluster: s.Current.Cluster, current: s.Current.ControlPlane.Object, desired: s.Desired.ControlPlane.Object, versionGetter: contract.ControlPlane().Version().Get, + ignorePaths: ignorePaths, }) if err != nil { // Best effort cleanup of the InfrastructureMachineTemplate (only on creation). @@ -427,7 +464,7 @@ func (r *Reconciler) reconcileMachineHealthCheck(ctx context.Context, current, d if err != nil { return errors.Wrapf(err, "failed to create patch helper for MachineHealthCheck %s", klog.KObj(desired)) } - if err := helper.Patch(ctx); err != nil { + if _, err := helper.Patch(ctx); err != nil { return errors.Wrapf(err, "failed to create MachineHealthCheck %s", klog.KObj(desired)) } r.recorder.Eventf(desired, corev1.EventTypeNormal, createEventReason, "Created MachineHealthCheck %q", klog.KObj(desired)) @@ -463,7 +500,7 @@ func (r *Reconciler) reconcileMachineHealthCheck(ctx context.Context, current, d } log.Info("Patching MachineHealthCheck") - if err := patchHelper.Patch(ctx); err != nil { + if _, err := patchHelper.Patch(ctx); err != nil { return errors.Wrapf(err, "failed to patch MachineHealthCheck %s", klog.KObj(current)) } r.recorder.Eventf(current, corev1.EventTypeNormal, updateEventReason, "Updated MachineHealthCheck %q", klog.KObj(current)) @@ -487,13 +524,15 @@ func (r *Reconciler) reconcileCluster(ctx context.Context, s *scope.Scope) error return nil } - changes := patchHelper.Changes() - if len(changes) == 0 { + diff := patchHelper.Diff() + patchData := patchHelper.PatchData() + if diff == "" && patchData == "" { log.Info("Patching Cluster") } else { - log.Info("Patching Cluster", "diff", string(changes)) + log.Info("Patching Cluster", "diff", diff, "patch", patchData) } - if err := patchHelper.Patch(ctx); err != nil { + modifiedResourceVersion, err := patchHelper.Patch(ctx) + if err != nil { return errors.Wrapf(err, "failed to patch Cluster %s", klog.KObj(s.Current.Cluster)) } r.recorder.Eventf(s.Current.Cluster, corev1.EventTypeNormal, updateEventReason, "Updated Cluster %q", klog.KObj(s.Current.Cluster)) @@ -501,21 +540,10 @@ func (r *Reconciler) reconcileCluster(ctx context.Context, s *scope.Scope) error // Wait until Cluster is updated in the cache. // Note: We have to do this because otherwise using a cached client in the Reconcile func could // return a stale state of the Cluster we just patched (because the cache might be stale). - // Note: It is good enough to check that the resource version changed. Other controllers might have updated the - // Cluster as well, but the combination of the patch call above without a conflict and a changed resource - // version here guarantees that we see the changes of our own update. - err = wait.PollUntilContextTimeout(ctx, 5*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { - key := client.ObjectKey{Namespace: s.Current.Cluster.GetNamespace(), Name: s.Current.Cluster.GetName()} - cachedCluster := &clusterv1.Cluster{} - if err := r.Client.Get(ctx, key, cachedCluster); err != nil { - return false, err - } - return s.Current.Cluster.GetResourceVersion() != cachedCluster.GetResourceVersion(), nil - }) - if err != nil { - return errors.Wrapf(err, "failed waiting for Cluster %s to be updated in the cache after patch", klog.KObj(s.Current.Cluster)) - } - return nil + // Note: Using DeepCopy to not modify s.Current.Cluster as it's not trivial to figure out what impact that would have. + cluster := s.Current.Cluster.DeepCopy() + cluster.ResourceVersion = modifiedResourceVersion + return clientutil.WaitForCacheToBeUpToDate(ctx, r.Client, "Cluster update", cluster) } // reconcileMachineDeployments reconciles the desired state of the MachineDeployment objects. @@ -666,7 +694,7 @@ func (r *Reconciler) createMachineDeployment(ctx context.Context, s *scope.Scope bootstrapCleanupFunc() return createErrorWithoutObjectName(ctx, err, md.Object) } - if err := helper.Patch(ctx); err != nil { + if _, err := helper.Patch(ctx); err != nil { // Best effort cleanup of the InfrastructureMachineTemplate & BootstrapTemplate (only on creation). infrastructureMachineCleanupFunc() bootstrapCleanupFunc() @@ -677,18 +705,8 @@ func (r *Reconciler) createMachineDeployment(ctx context.Context, s *scope.Scope // Wait until MachineDeployment is visible in the cache. // Note: We have to do this because otherwise using a cached client in current state could // miss a newly created MachineDeployment (because the cache might be stale). - err = wait.PollUntilContextTimeout(ctx, 5*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { - key := client.ObjectKey{Namespace: md.Object.Namespace, Name: md.Object.Name} - if err := r.Client.Get(ctx, key, &clusterv1.MachineDeployment{}); err != nil { - if apierrors.IsNotFound(err) { - return false, nil - } - return false, err - } - return true, nil - }) - if err != nil { - return errors.Wrapf(err, "failed waiting for MachineDeployment %s to be visible in the cache after create", md.Object.Kind) + if err := clientutil.WaitForObjectsToBeAddedToTheCache(ctx, r.Client, "MachineDeployment creation", md.Object); err != nil { + return err } // If the MachineDeployment has defined a MachineHealthCheck reconcile it. @@ -791,13 +809,15 @@ func (r *Reconciler) updateMachineDeployment(ctx context.Context, s *scope.Scope return nil } - changes := patchHelper.Changes() - if len(changes) == 0 { + diff := patchHelper.Diff() + patchData := patchHelper.PatchData() + if diff == "" && patchData == "" { log.Info("Patching MachineDeployment") } else { - log.Info("Patching MachineDeployment", "diff", string(changes)) + log.Info("Patching MachineDeployment", "diff", diff, "patch", patchData) } - if err := patchHelper.Patch(ctx); err != nil { + modifiedResourceVersion, err := patchHelper.Patch(ctx) + if err != nil { // Best effort cleanup of the InfrastructureMachineTemplate & BootstrapTemplate (only on template rotation). infrastructureMachineCleanupFunc() bootstrapCleanupFunc() @@ -808,19 +828,11 @@ func (r *Reconciler) updateMachineDeployment(ctx context.Context, s *scope.Scope // Wait until MachineDeployment is updated in the cache. // Note: We have to do this because otherwise using a cached client in current state could // return a stale state of a MachineDeployment we just patched (because the cache might be stale). - // Note: It is good enough to check that the resource version changed. Other controllers might have updated the - // MachineDeployment as well, but the combination of the patch call above without a conflict and a changed resource - // version here guarantees that we see the changes of our own update. - err = wait.PollUntilContextTimeout(ctx, 5*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { - key := client.ObjectKey{Namespace: currentMD.Object.GetNamespace(), Name: currentMD.Object.GetName()} - cachedMD := &clusterv1.MachineDeployment{} - if err := r.Client.Get(ctx, key, cachedMD); err != nil { - return false, err - } - return currentMD.Object.GetResourceVersion() != cachedMD.GetResourceVersion(), nil - }) - if err != nil { - return errors.Wrapf(err, "failed waiting for MachineDeployment %s to be updated in the cache after patch", klog.KObj(currentMD.Object)) + // Note: Using DeepCopy to not modify currentMD.Object as it's not trivial to figure out what impact that would have. + md := currentMD.Object.DeepCopy() + md.ResourceVersion = modifiedResourceVersion + if err := clientutil.WaitForCacheToBeUpToDate(ctx, r.Client, "MachineDeployment update", md); err != nil { + return err } // We want to call both cleanup functions even if one of them fails to clean up as much as possible. @@ -1007,7 +1019,7 @@ func (r *Reconciler) createMachinePool(ctx context.Context, s *scope.Scope, mp * bootstrapCleanupFunc() return createErrorWithoutObjectName(ctx, err, mp.Object) } - if err := helper.Patch(ctx); err != nil { + if _, err := helper.Patch(ctx); err != nil { // Best effort cleanup of the InfrastructureMachinePool & BootstrapConfig (only on creation). infrastructureMachineMachinePoolCleanupFunc() bootstrapCleanupFunc() @@ -1018,21 +1030,7 @@ func (r *Reconciler) createMachinePool(ctx context.Context, s *scope.Scope, mp * // Wait until MachinePool is visible in the cache. // Note: We have to do this because otherwise using a cached client in current state could // miss a newly created MachinePool (because the cache might be stale). - err = wait.PollUntilContextTimeout(ctx, 5*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { - key := client.ObjectKey{Namespace: mp.Object.Namespace, Name: mp.Object.Name} - if err := r.Client.Get(ctx, key, &clusterv1.MachinePool{}); err != nil { - if apierrors.IsNotFound(err) { - return false, nil - } - return false, err - } - return true, nil - }) - if err != nil { - return errors.Wrapf(err, "failed waiting for MachinePool %s to be visible in the cache after create", mp.Object.Kind) - } - - return nil + return clientutil.WaitForObjectsToBeAddedToTheCache(ctx, r.Client, "MachinePool creation", mp.Object) } // updateMachinePool updates a MachinePool. Also updates the corresponding objects if necessary. @@ -1076,13 +1074,15 @@ func (r *Reconciler) updateMachinePool(ctx context.Context, s *scope.Scope, mpTo return nil } - changes := patchHelper.Changes() - if len(changes) == 0 { + diff := patchHelper.Diff() + patchData := patchHelper.PatchData() + if diff == "" && patchData == "" { log.Info("Patching MachinePool") } else { - log.Info("Patching MachinePool", "diff", string(changes)) + log.Info("Patching MachinePool", "diff", diff, "patch", patchData) } - if err := patchHelper.Patch(ctx); err != nil { + modifiedResourceVersion, err := patchHelper.Patch(ctx) + if err != nil { return errors.Wrapf(err, "failed to patch MachinePool %s", klog.KObj(currentMP.Object)) } r.recorder.Eventf(cluster, corev1.EventTypeNormal, updateEventReason, "Updated MachinePool %q%s", klog.KObj(currentMP.Object), logMachinePoolVersionChange(currentMP.Object, desiredMP.Object)) @@ -1093,16 +1093,10 @@ func (r *Reconciler) updateMachinePool(ctx context.Context, s *scope.Scope, mpTo // Note: It is good enough to check that the resource version changed. Other controllers might have updated the // MachinePool as well, but the combination of the patch call above without a conflict and a changed resource // version here guarantees that we see the changes of our own update. - err = wait.PollUntilContextTimeout(ctx, 5*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { - key := client.ObjectKey{Namespace: currentMP.Object.GetNamespace(), Name: currentMP.Object.GetName()} - cachedMP := &clusterv1.MachinePool{} - if err := r.Client.Get(ctx, key, cachedMP); err != nil { - return false, err - } - return currentMP.Object.GetResourceVersion() != cachedMP.GetResourceVersion(), nil - }) - if err != nil { - return errors.Wrapf(err, "failed waiting for MachinePool %s to be updated in the cache after patch", klog.KObj(currentMP.Object)) + mp := currentMP.Object.DeepCopy() + mp.ResourceVersion = modifiedResourceVersion + if err := clientutil.WaitForCacheToBeUpToDate(ctx, r.Client, "MachinePool update", mp); err != nil { + return err } // We want to call both cleanup functions even if one of them fails to clean up as much as possible. @@ -1206,7 +1200,7 @@ func (r *Reconciler) reconcileReferencedObject(ctx context.Context, in reconcile if err != nil { return false, errors.Wrap(createErrorWithoutObjectName(ctx, err, in.desired), "failed to create patch helper") } - if err := helper.Patch(ctx); err != nil { + if _, err := helper.Patch(ctx); err != nil { return false, createErrorWithoutObjectName(ctx, err, in.desired) } r.recorder.Eventf(in.cluster, corev1.EventTypeNormal, createEventReason, "Created %s %q", in.desired.GetKind(), klog.KObj(in.desired)) @@ -1231,13 +1225,14 @@ func (r *Reconciler) reconcileReferencedObject(ctx context.Context, in reconcile return false, nil } - changes := patchHelper.Changes() - if len(changes) == 0 { + diff := patchHelper.Diff() + patchData := patchHelper.PatchData() + if diff == "" && patchData == "" { log.Info(fmt.Sprintf("Patching %s", in.desired.GetKind())) } else { - log.Info(fmt.Sprintf("Patching %s", in.desired.GetKind()), "diff", string(changes)) + log.Info(fmt.Sprintf("Patching %s", in.desired.GetKind()), "diff", diff, "patch", patchData) } - if err := patchHelper.Patch(ctx); err != nil { + if _, err := patchHelper.Patch(ctx); err != nil { return false, errors.Wrapf(err, "failed to patch %s %s", in.current.GetKind(), klog.KObj(in.current)) } r.recorder.Eventf(in.cluster, corev1.EventTypeNormal, updateEventReason, "Updated %s %q%s", in.desired.GetKind(), klog.KObj(in.desired), logUnstructuredVersionChange(in.current, in.desired, in.versionGetter)) @@ -1292,7 +1287,7 @@ func (r *Reconciler) reconcileReferencedTemplate(ctx context.Context, in reconci if err != nil { return false, errors.Wrap(createErrorWithoutObjectName(ctx, err, in.desired), "failed to create patch helper") } - if err := helper.Patch(ctx); err != nil { + if _, err := helper.Patch(ctx); err != nil { return false, createErrorWithoutObjectName(ctx, err, in.desired) } r.recorder.Eventf(in.cluster, corev1.EventTypeNormal, createEventReason, "Created %s %q", in.desired.GetKind(), klog.KObj(in.desired)) @@ -1326,13 +1321,14 @@ func (r *Reconciler) reconcileReferencedTemplate(ctx context.Context, in reconci // If there are no changes in the spec, and thus only changes in metadata, instead of doing a full template // rotation we patch the object in place. This avoids recreating machines. if !patchHelper.HasSpecChanges() { - changes := patchHelper.Changes() - if len(changes) == 0 { + diff := patchHelper.Diff() + patchData := patchHelper.PatchData() + if diff == "" && patchData == "" { log.Info(fmt.Sprintf("Patching %s", in.desired.GetKind())) } else { - log.Info(fmt.Sprintf("Patching %s", in.desired.GetKind()), "diff", string(changes)) + log.Info(fmt.Sprintf("Patching %s", in.desired.GetKind()), "diff", diff, "patch", patchData) } - if err := patchHelper.Patch(ctx); err != nil { + if _, err := patchHelper.Patch(ctx); err != nil { return false, errors.Wrapf(err, "failed to patch %s %s", in.desired.GetKind(), klog.KObj(in.desired)) } r.recorder.Eventf(in.cluster, corev1.EventTypeNormal, updateEventReason, "Updated %s %q (metadata changes)", in.desired.GetKind(), klog.KObj(in.desired)) @@ -1346,18 +1342,19 @@ func (r *Reconciler) reconcileReferencedTemplate(ctx context.Context, in reconci newName := names.SimpleNameGenerator.GenerateName(in.templateNamePrefix) in.desired.SetName(newName) - changes := patchHelper.Changes() - if len(changes) == 0 { + diff := patchHelper.Diff() + patchData := patchHelper.PatchData() + if diff == "" && patchData == "" { log.Info(fmt.Sprintf("Rotating %s, new name %s", in.current.GetKind(), newName)) } else { - log.Info(fmt.Sprintf("Rotating %s, new name %s", in.current.GetKind(), newName), "diff", string(changes)) + log.Info(fmt.Sprintf("Rotating %s, new name %s", in.current.GetKind(), newName), "diff", diff, "patch", patchData) } log.Info(fmt.Sprintf("Creating %s", in.current.GetKind())) helper, err := structuredmerge.NewServerSidePatchHelper(ctx, nil, in.desired, r.Client, r.ssaCache) if err != nil { return false, errors.Wrap(createErrorWithoutObjectName(ctx, err, in.desired), "failed to create patch helper") } - if err := helper.Patch(ctx); err != nil { + if _, err := helper.Patch(ctx); err != nil { return false, createErrorWithoutObjectName(ctx, err, in.desired) } r.recorder.Eventf(in.cluster, corev1.EventTypeNormal, createEventReason, "Created %s %q as a replacement for %q (template rotation)", in.desired.GetKind(), klog.KObj(in.desired), in.ref.Name) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/dryrun.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/dryrun.go index c691a9d25b..f478ee87e6 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/dryrun.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/dryrun.go @@ -19,12 +19,15 @@ package structuredmerge import ( "context" "encoding/json" + "strings" jsonpatch "github.com/evanphx/json-patch/v5" + "github.com/google/go-cmp/cmp" "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/yaml" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/internal/contract" @@ -47,45 +50,44 @@ type dryRunSSAPatchInput struct { } // dryRunSSAPatch uses server side apply dry run to determine if the operation is going to change the actual object. -func dryRunSSAPatch(ctx context.Context, dryRunCtx *dryRunSSAPatchInput) (bool, bool, []byte, error) { +func dryRunSSAPatch(ctx context.Context, dryRunCtx *dryRunSSAPatchInput) (bool, bool, string, string, error) { // Compute a request identifier. // The identifier is unique for a specific request to ensure we don't have to re-run the request // once we found out that it would not produce a diff. // The identifier consists of: gvk, namespace, name and resourceVersion of originalUnstructured // and a hash of modifiedUnstructured. // This ensures that we re-run the request as soon as either original or modified changes. - requestIdentifier, err := ssa.ComputeRequestIdentifier(dryRunCtx.client.Scheme(), dryRunCtx.originalUnstructured, dryRunCtx.modifiedUnstructured) + requestIdentifier, err := ssa.ComputeRequestIdentifier(dryRunCtx.client.Scheme(), dryRunCtx.originalUnstructured.GetResourceVersion(), dryRunCtx.modifiedUnstructured) if err != nil { - return false, false, nil, err + return false, false, "", "", err } // Check if we already ran this request before by checking if the cache already contains this identifier. // Note: We only add an identifier to the cache if the result of the dry run was no diff. if exists := dryRunCtx.ssaCache.Has(requestIdentifier, dryRunCtx.originalUnstructured.GetKind()); exists { - return false, false, nil, nil + return false, false, "", "", nil } // For dry run we use the same options as for the intent but with adding metadata.managedFields // to ensure that changes to ownership are detected. filterObjectInput := &ssa.FilterObjectInput{ - AllowedPaths: append(dryRunCtx.helperOptions.AllowedPaths, []string{"metadata", "managedFields"}), - IgnorePaths: dryRunCtx.helperOptions.IgnorePaths, - DropEmptyStructAndNil: dryRunCtx.helperOptions.DropEmptyStructAndNil, + AllowedPaths: append(dryRunCtx.helperOptions.AllowedPaths, []string{"metadata", "managedFields"}), + IgnorePaths: dryRunCtx.helperOptions.IgnorePaths, } // Add TopologyDryRunAnnotation to notify validation webhooks to skip immutability checks. if err := unstructured.SetNestedField(dryRunCtx.originalUnstructured.Object, "", "metadata", "annotations", clusterv1.TopologyDryRunAnnotation); err != nil { - return false, false, nil, errors.Wrap(err, "failed to add topology dry-run annotation to original object") + return false, false, "", "", errors.Wrap(err, "failed to add topology dry-run annotation to original object") } if err := unstructured.SetNestedField(dryRunCtx.modifiedUnstructured.Object, "", "metadata", "annotations", clusterv1.TopologyDryRunAnnotation); err != nil { - return false, false, nil, errors.Wrap(err, "failed to add topology dry-run annotation to modified object") + return false, false, "", "", errors.Wrap(err, "failed to add topology dry-run annotation to modified object") } // Do a server-side apply dry-run with modifiedUnstructured to get the updated object. - err = dryRunCtx.client.Patch(ctx, dryRunCtx.modifiedUnstructured, client.Apply, client.DryRunAll, client.FieldOwner(TopologyManagerName), client.ForceOwnership) + err = dryRunCtx.client.Apply(ctx, client.ApplyConfigurationFromUnstructured(dryRunCtx.modifiedUnstructured), client.DryRunAll, client.FieldOwner(TopologyManagerName), client.ForceOwnership) if err != nil { // This catches errors like metadata.uid changes. - return false, false, nil, errors.Wrap(err, "server side apply dry-run failed for modified object") + return false, false, "", "", errors.Wrap(err, "server side apply dry-run failed for modified object") } // Do a server-side apply dry-run with originalUnstructured to ensure the latest defaulting is applied. @@ -108,9 +110,9 @@ func dryRunSSAPatch(ctx context.Context, dryRunCtx *dryRunSSAPatchInput) (bool, // Note: Otherwise we would get the following error: // "failed to request dry-run server side apply: metadata.managedFields must be nil" dryRunCtx.originalUnstructured.SetManagedFields(nil) - err = dryRunCtx.client.Patch(ctx, dryRunCtx.originalUnstructured, client.Apply, client.DryRunAll, client.FieldOwner(TopologyManagerName), client.ForceOwnership) + err = dryRunCtx.client.Apply(ctx, client.ApplyConfigurationFromUnstructured(dryRunCtx.originalUnstructured), client.DryRunAll, client.FieldOwner(TopologyManagerName), client.ForceOwnership) if err != nil { - return false, false, nil, errors.Wrap(err, "server side apply dry-run failed for original object") + return false, false, "", "", errors.Wrap(err, "server side apply dry-run failed for original object") } // Restore managed fields. dryRunCtx.originalUnstructured.SetManagedFields(originalUnstructuredManagedFieldsBeforeSSA) @@ -125,7 +127,7 @@ func dryRunSSAPatch(ctx context.Context, dryRunCtx *dryRunSSAPatchInput) (bool, // Please note that if other managers made changes to fields that we care about and thus ownership changed, // this would affect our managed fields as well and we would still detect it by diffing our managed fields. if err := cleanupManagedFieldsAndAnnotation(dryRunCtx.modifiedUnstructured); err != nil { - return false, false, nil, errors.Wrap(err, "failed to filter topology dry-run annotation on modified object") + return false, false, "", "", errors.Wrap(err, "failed to filter topology dry-run annotation on modified object") } // Also run the function for the originalUnstructured to remove the managedField @@ -136,7 +138,7 @@ func dryRunSSAPatch(ctx context.Context, dryRunCtx *dryRunSSAPatchInput) (bool, // Please note that if other managers made changes to fields that we care about and thus ownership changed, // this would affect our managed fields as well and we would still detect it by diffing our managed fields. if err := cleanupManagedFieldsAndAnnotation(dryRunCtx.originalUnstructured); err != nil { - return false, false, nil, errors.Wrap(err, "failed to filter topology dry-run annotation on original object") + return false, false, "", "", errors.Wrap(err, "failed to filter topology dry-run annotation on original object") } // Drop the other fields which are not part of our intent. @@ -146,28 +148,28 @@ func dryRunSSAPatch(ctx context.Context, dryRunCtx *dryRunSSAPatchInput) (bool, // Compare the output of dry run to the original object. originalJSON, err := json.Marshal(dryRunCtx.originalUnstructured) if err != nil { - return false, false, nil, err + return false, false, "", "", err } modifiedJSON, err := json.Marshal(dryRunCtx.modifiedUnstructured) if err != nil { - return false, false, nil, err + return false, false, "", "", err } rawDiff, err := jsonpatch.CreateMergePatch(originalJSON, modifiedJSON) if err != nil { - return false, false, nil, err + return false, false, "", "", err } // Determine if there are changes to the spec and object. diff := &unstructured.Unstructured{} if err := json.Unmarshal(rawDiff, &diff.Object); err != nil { - return false, false, nil, err + return false, false, "", "", err } hasChanges := len(diff.Object) > 0 _, hasSpecChanges := diff.Object["spec"] - var changes []byte + var patchString, diffString string if hasChanges { // Cleanup diff by dropping .metadata.managedFields. ssa.FilterIntent(&ssa.FilterIntentInput{ @@ -178,10 +180,30 @@ func dryRunSSAPatch(ctx context.Context, dryRunCtx *dryRunSSAPatchInput) (bool, // changes should be empty (not "{}") if diff.Object is empty if len(diff.Object) != 0 { - changes, err = json.Marshal(diff.Object) + patchBytes, err := json.Marshal(diff.Object) if err != nil { - return false, false, nil, errors.Wrapf(err, "failed to marshal diff") + return false, false, "", "", errors.Wrapf(err, "failed to marshal diff") } + patchString = string(patchBytes) + + originalJSONWithChanges, err := jsonpatch.MergePatch(originalJSON, patchBytes) + if err != nil { + return false, false, "", "", errors.Wrapf(err, "failed to apply diff to original object") + } + + originalYAML, err := yaml.JSONToYAML(originalJSON) + if err != nil { + return false, false, "", "", errors.Wrapf(err, "failed to convert original object to yaml") + } + + originalYAMLWithChanges, err := yaml.JSONToYAML(originalJSONWithChanges) + if err != nil { + return false, false, "", "", errors.Wrapf(err, "failed to convert original object with diff to yaml") + } + + diffString = cmp.Diff(string(originalYAML), string(originalYAMLWithChanges)) + diffString = strings.ReplaceAll(diffString, "\u00A0", " ") // No-Break Space (NBSP) + diffString = strings.ReplaceAll(diffString, "\t", " ") } } @@ -190,7 +212,7 @@ func dryRunSSAPatch(ctx context.Context, dryRunCtx *dryRunSSAPatchInput) (bool, dryRunCtx.ssaCache.Add(requestIdentifier) } - return hasChanges, hasSpecChanges, changes, nil + return hasChanges, hasSpecChanges, patchString, diffString, nil } // cleanupManagedFieldsAndAnnotation adjusts the obj to remove the topology.cluster.x-k8s.io/dry-run diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/interfaces.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/interfaces.go index 2842ca4e7b..15c6ad915c 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/interfaces.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/interfaces.go @@ -37,9 +37,12 @@ type PatchHelper interface { // HasSpecChanges return true if the modified object is generating spec changes vs the original object. HasSpecChanges() bool - // Changes returns the changes vs the original object. - Changes() []byte + // PatchData return the patch that will be applied. + PatchData() string + + // Diff return the diff between original and modified. + Diff() string // Patch patches the given obj in the Kubernetes cluster. - Patch(ctx context.Context) error + Patch(ctx context.Context) (modifiedResourceVersion string, err error) } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/options.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/options.go index 8fa59d8a70..b5a8ccaedf 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/options.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/options.go @@ -17,7 +17,6 @@ limitations under the License. package structuredmerge import ( - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/controller-runtime/pkg/client" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" @@ -53,6 +52,7 @@ var ( {"kind"}, {"metadata", "name"}, {"metadata", "namespace"}, + {"metadata", "annotations", clusterv1.ClusterTopologyUpgradeStepAnnotation}, // uid is optional for a server side apply intent but sets the expectation of an object getting created or a specific one updated. {"metadata", "uid"}, // the topology controller controls/has an opinion for the labels ClusterNameLabel @@ -79,27 +79,15 @@ type HelperOptions struct { func newHelperOptions(target client.Object, opts ...HelperOption) *HelperOptions { helperOptions := &HelperOptions{ FilterObjectInput: ssa.FilterObjectInput{ - AllowedPaths: defaultAllowedPaths, - IgnorePaths: []contract.Path{}, - DropEmptyStructAndNil: false, + AllowedPaths: defaultAllowedPaths, + IgnorePaths: []contract.Path{}, }, } // Overwrite the allowedPaths for Cluster objects to prevent the topology controller // to take ownership of fields it is not supposed to. - switch target.(type) { - case *clusterv1.Cluster: + if _, ok := target.(*clusterv1.Cluster); ok { helperOptions.AllowedPaths = allowedPathsCluster - helperOptions.DropEmptyStructAndNil = true - // NOTE: DropEmptyStructAndNil is required for Cluster, because it is converted to unstructured using the DefaultUnstructuredConverter, - // and it does not handle omitzero (yet). - case *unstructured.Unstructured: - // NOTE: DropEmptyStructAndNil is not required for unstructured objects, because DefaultUnstructuredConverter is not called. - default: - helperOptions.DropEmptyStructAndNil = true - // NOTE: DropEmptyStructAndNil is required for typed objects, because they are converted to unstructured using the DefaultUnstructuredConverter, - // and it does not handle omitzero (yet). } - helperOptions = helperOptions.ApplyOptions(opts) return helperOptions } @@ -122,13 +110,3 @@ type IgnorePaths []contract.Path func (i IgnorePaths) ApplyToHelper(opts *HelperOptions) { opts.IgnorePaths = i } - -// DropEmptyStructAndNil instructs the Helper to drop all fields with values equal to empty struct or nil. -// NOTE: This is required when using typed objects, because the DefaultUnstructuredConverter does -// not handle omitzero (yet). -type DropEmptyStructAndNil bool - -// ApplyToHelper applies this configuration to the given helper options. -func (i DropEmptyStructAndNil) ApplyToHelper(opts *HelperOptions) { - opts.DropEmptyStructAndNil = bool(i) -} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/serversidepathhelper.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/serversidepathhelper.go index 5a8b04c67f..54c927a9d4 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/serversidepathhelper.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge/serversidepathhelper.go @@ -36,7 +36,8 @@ type serverSidePatchHelper struct { modified *unstructured.Unstructured hasChanges bool hasSpecChanges bool - changes []byte + patch string + diff string } // NewServerSidePatchHelper returns a new PatchHelper using server side apply. @@ -90,13 +91,13 @@ func NewServerSidePatchHelper(ctx context.Context, original, modified client.Obj // Determine if the intent defined in the modified object is going to trigger // an actual change when running server side apply, and if this change might impact the object spec or not. var hasChanges, hasSpecChanges bool - var changes []byte + var patch, diff string switch { case util.IsNil(original): hasChanges, hasSpecChanges = true, true default: var err error - hasChanges, hasSpecChanges, changes, err = dryRunSSAPatch(ctx, &dryRunSSAPatchInput{ + hasChanges, hasSpecChanges, patch, diff, err = dryRunSSAPatch(ctx, &dryRunSSAPatchInput{ client: c, ssaCache: ssaCache, originalUnstructured: originalUnstructured, @@ -113,7 +114,8 @@ func NewServerSidePatchHelper(ctx context.Context, original, modified client.Obj modified: modifiedUnstructured, hasChanges: hasChanges, hasSpecChanges: hasSpecChanges, - changes: changes, + patch: patch, + diff: diff, }, nil } @@ -122,9 +124,14 @@ func (h *serverSidePatchHelper) HasSpecChanges() bool { return h.hasSpecChanges } -// Changes return the changes. -func (h *serverSidePatchHelper) Changes() []byte { - return h.changes +// PatchData return the patch that will be applied. +func (h *serverSidePatchHelper) PatchData() string { + return h.patch +} + +// Diff return the diff between original and modified. +func (h *serverSidePatchHelper) Diff() string { + return h.diff } // HasChanges return true if the patch has changes. @@ -133,19 +140,22 @@ func (h *serverSidePatchHelper) HasChanges() bool { } // Patch will server side apply the current intent (the modified object. -func (h *serverSidePatchHelper) Patch(ctx context.Context) error { +func (h *serverSidePatchHelper) Patch(ctx context.Context) (string, error) { if !h.HasChanges() { - return nil + return "", nil } log := ctrl.LoggerFrom(ctx) log.V(5).Info("Patching object", "intent", h.modified) - options := []client.PatchOption{ + options := []client.ApplyOption{ client.FieldOwner(TopologyManagerName), // NOTE: we are using force ownership so in case of conflicts the topology controller // overwrite values and become sole manager. client.ForceOwnership, } - return h.client.Patch(ctx, h.modified, client.Apply, options...) + if err := h.client.Apply(ctx, client.ApplyConfigurationFromUnstructured(h.modified), options...); err != nil { + return "", err + } + return h.modified.GetResourceVersion(), nil } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/machinedeployment/machinedeployment_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/machinedeployment/machinedeployment_controller.go index 23875c76d6..2ab4dd9851 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/machinedeployment/machinedeployment_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/machinedeployment/machinedeployment_controller.go @@ -33,6 +33,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/internal/controllers/topology/machineset" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/finalizers" @@ -69,12 +70,11 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt return err } - err = ctrl.NewControllerManagedBy(mgr). + err = capicontrollerutil.NewControllerManagedBy(mgr, predicateLog). For(&clusterv1.MachineDeployment{}, builder.WithPredicates( - predicates.All(mgr.GetScheme(), predicateLog, - predicates.ResourceIsTopologyOwned(mgr.GetScheme(), predicateLog), - predicates.ResourceNotPaused(mgr.GetScheme(), predicateLog)), + predicates.ResourceIsTopologyOwned(mgr.GetScheme(), predicateLog), + predicates.ResourceNotPaused(mgr.GetScheme(), predicateLog), ), ). Named("topology/machinedeployment"). @@ -83,13 +83,8 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt Watches( &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(clusterToMachineDeployments), - builder.WithPredicates( - predicates.All(mgr.GetScheme(), predicateLog, - predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog), - predicates.ClusterUnpaused(mgr.GetScheme(), predicateLog), - predicates.ClusterHasTopology(mgr.GetScheme(), predicateLog), - ), - ), + predicates.ClusterUnpaused(mgr.GetScheme(), predicateLog), + predicates.ClusterHasTopology(mgr.GetScheme(), predicateLog), ). Complete(r) if err != nil { diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/machineset/machineset_controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/machineset/machineset_controller.go index 0a90ffef11..2d17236e2d 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/machineset/machineset_controller.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/controllers/topology/machineset/machineset_controller.go @@ -19,12 +19,14 @@ package machineset import ( "context" "fmt" + "time" "github.com/pkg/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" - kerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/util/retry" "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" @@ -34,12 +36,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + capicontrollerutil "sigs.k8s.io/cluster-api/internal/util/controller" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/finalizers" "sigs.k8s.io/cluster-api/util/labels" clog "sigs.k8s.io/cluster-api/util/log" - "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/cluster-api/util/predicates" ) @@ -71,13 +73,11 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt return err } - err = ctrl.NewControllerManagedBy(mgr). + err = capicontrollerutil.NewControllerManagedBy(mgr, predicateLog). For(&clusterv1.MachineSet{}, builder.WithPredicates( - predicates.All(mgr.GetScheme(), predicateLog, - predicates.ResourceIsTopologyOwned(mgr.GetScheme(), predicateLog), - predicates.ResourceNotPaused(mgr.GetScheme(), predicateLog)), - ), + predicates.ResourceIsTopologyOwned(mgr.GetScheme(), predicateLog), + predicates.ResourceNotPaused(mgr.GetScheme(), predicateLog)), ). Named("topology/machineset"). WithOptions(options). @@ -85,13 +85,8 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt Watches( &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(clusterToMachineSets), - builder.WithPredicates( - predicates.All(mgr.GetScheme(), predicateLog, - predicates.ResourceIsChanged(mgr.GetScheme(), predicateLog), - predicates.ClusterUnpaused(mgr.GetScheme(), predicateLog), - predicates.ClusterHasTopology(mgr.GetScheme(), predicateLog), - ), - ), + predicates.ClusterUnpaused(mgr.GetScheme(), predicateLog), + predicates.ClusterHasTopology(mgr.GetScheme(), predicateLog), ). Complete(r) if err != nil { @@ -159,17 +154,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re return ctrl.Result{}, nil } - // Create a patch helper to add or remove the finalizer from the MachineSet. - patchHelper, err := patch.NewHelper(ms, r.Client) - if err != nil { - return ctrl.Result{}, err - } - defer func() { - if err := patchHelper.Patch(ctx, ms); err != nil { - reterr = kerrors.NewAggregate([]error{reterr, err}) - } - }() - // Handle deletion reconciliation loop. if !ms.DeletionTimestamp.IsZero() { return ctrl.Result{}, r.reconcileDelete(ctx, ms) @@ -221,10 +205,26 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, ms *clusterv1.MachineS return errors.Wrapf(err, "failed to delete %s %s for MachineSet %s", ref.Kind, klog.KRef(ms.Namespace, ref.Name), klog.KObj(ms)) } - // Remove the finalizer so the MachineSet can be garbage collected by Kubernetes. - controllerutil.RemoveFinalizer(ms, clusterv1.MachineSetTopologyFinalizer) + // Note: It can happen that both MachineSet controllers are going through reconcileDelete at the same time. + // If the MachineSet does not have any Machines anymore at this point, the topology/machineset controller will + // likely complete reconcileDelete after the regular MachineSet controller. In that case it might happen + // that removing the finalizer here would try to re-add the finalizer of the other controller and then the + // request to the apiserver fails with: "Forbidden: no new finalizers can be added if the object is being deleted" + msKey := client.ObjectKeyFromObject(ms) + return retry.RetryOnConflict(wait.Backoff{ + Steps: 3, + Duration: 50 * time.Millisecond, + Factor: 1.0, + Jitter: 0.1, + }, func() error { + if err := r.Client.Get(ctx, msKey, ms); err != nil { + return client.IgnoreNotFound(err) + } - return nil + orig := ms.DeepCopy() + controllerutil.RemoveFinalizer(ms, clusterv1.MachineSetTopologyFinalizer) + return client.IgnoreNotFound(r.Client.Patch(ctx, ms, client.MergeFrom(orig))) + }) } // getMachineDeploymentName calculates the MachineDeployment name based on owner references. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/hooks/tracking.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/hooks/tracking.go index f32ddd90fa..0df9a2c72d 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/hooks/tracking.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/hooks/tracking.go @@ -29,35 +29,57 @@ import ( runtimev1 "sigs.k8s.io/cluster-api/api/runtime/v1beta2" runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" - "sigs.k8s.io/cluster-api/util/patch" ) // MarkAsPending adds to the object's PendingHooksAnnotation the intent to execute a hook after an operation completes. // Usually this function is called when an operation is starting in order to track the intent to call an After hook later in the process. -func MarkAsPending(ctx context.Context, c client.Client, obj client.Object, hooks ...runtimecatalog.Hook) error { +func MarkAsPending(ctx context.Context, c client.Client, obj client.Object, updateResourceVersionOnObject bool, hooks ...runtimecatalog.Hook) error { hookNames := []string{} for _, hook := range hooks { hookNames = append(hookNames, runtimecatalog.HookName(hook)) } - patchHelper, err := patch.NewHelper(obj, c) - if err != nil { + orig := obj.DeepCopyObject().(client.Object) + + if changed := MarkObjectAsPending(obj, hooks...); !changed { + return nil + } + + // In some cases it is preferred to not update resourceVersion in the input object, + // because this could lead to conflict errors e.g. when patching at the end of a reconcile loop. + if !updateResourceVersionOnObject { + obj = obj.DeepCopyObject().(client.Object) + } + if err := c.Patch(ctx, obj, client.MergeFrom(orig)); err != nil { return errors.Wrapf(err, "failed to mark %q hook(s) as pending", strings.Join(hookNames, ",")) } + return nil +} + +// MarkObjectAsPending adds to the object's PendingHooksAnnotation the intent to execute a hook after an operation completes. +// Usually this function is called when an operation is starting in order to track the intent to call an After hook later in the process. +func MarkObjectAsPending(obj client.Object, hooks ...runtimecatalog.Hook) (changed bool) { + hookNames := []string{} + for _, hook := range hooks { + hookNames = append(hookNames, runtimecatalog.HookName(hook)) + } + // Read the annotation of the objects and add the hook to the comma separated list annotations := obj.GetAnnotations() if annotations == nil { annotations = map[string]string{} } - annotations[runtimev1.PendingHooksAnnotation] = addToCommaSeparatedList(annotations[runtimev1.PendingHooksAnnotation], hookNames...) - obj.SetAnnotations(annotations) - if err := patchHelper.Patch(ctx, obj); err != nil { - return errors.Wrapf(err, "failed to mark %q hook(s) as pending", strings.Join(hookNames, ",")) + newAnnotationValue := addToCommaSeparatedList(annotations[runtimev1.PendingHooksAnnotation], hookNames...) + + if annotations[runtimev1.PendingHooksAnnotation] == newAnnotationValue { + return false } - return nil + annotations[runtimev1.PendingHooksAnnotation] = newAnnotationValue + obj.SetAnnotations(annotations) + return true } // IsPending returns true if there is an intent to call a hook being tracked in the object's PendingHooksAnnotation. @@ -73,30 +95,33 @@ func IsPending(hook runtimecatalog.Hook, obj client.Object) bool { // MarkAsDone removes the intent to call a Hook from the object's PendingHooksAnnotation. // Usually this func is called after all the registered extensions for the Hook returned an answer without requests // to hold on to the object's lifecycle (retryAfterSeconds). -func MarkAsDone(ctx context.Context, c client.Client, obj client.Object, hooks ...runtimecatalog.Hook) error { - hookNames := []string{} - for _, hook := range hooks { - hookNames = append(hookNames, runtimecatalog.HookName(hook)) +func MarkAsDone(ctx context.Context, c client.Client, obj client.Object, updateResourceVersionOnObject bool, hook runtimecatalog.Hook) error { + if !IsPending(hook, obj) { + return nil } - patchHelper, err := patch.NewHelper(obj, c) - if err != nil { - return errors.Wrapf(err, "failed to mark %q hook(s) as done", strings.Join(hookNames, ",")) - } + hookName := runtimecatalog.HookName(hook) + + orig := obj.DeepCopyObject().(client.Object) // Read the annotation of the objects and add the hook to the comma separated list annotations := obj.GetAnnotations() if annotations == nil { annotations = map[string]string{} } - annotations[runtimev1.PendingHooksAnnotation] = removeFromCommaSeparatedList(annotations[runtimev1.PendingHooksAnnotation], hookNames...) + annotations[runtimev1.PendingHooksAnnotation] = removeFromCommaSeparatedList(annotations[runtimev1.PendingHooksAnnotation], hookName) if annotations[runtimev1.PendingHooksAnnotation] == "" { delete(annotations, runtimev1.PendingHooksAnnotation) } obj.SetAnnotations(annotations) - if err := patchHelper.Patch(ctx, obj); err != nil { - return errors.Wrapf(err, "failed to mark %q hook(s) as done", strings.Join(hookNames, ",")) + // In some cases it is preferred to not update resourceVersion in the input object, + // because this could lead to conflict errors e.g. when patching at the end of a reconcile loop. + if !updateResourceVersionOnObject { + obj = obj.DeepCopyObject().(client.Object) + } + if err := c.Patch(ctx, obj, client.MergeFrom(orig)); err != nil { + return errors.Wrapf(err, "failed to mark %q hook as done", hookName) } return nil @@ -115,16 +140,17 @@ func IsOkToDelete(obj client.Object) bool { } // MarkAsOkToDelete adds the OkToDeleteAnnotation annotation to the object and patches it. -func MarkAsOkToDelete(ctx context.Context, c client.Client, obj client.Object) error { +func MarkAsOkToDelete(ctx context.Context, c client.Client, obj client.Object, updateResourceVersionOnObject bool) error { + if _, ok := obj.GetAnnotations()[runtimev1.OkToDeleteAnnotation]; ok { + return nil + } + gvk, err := apiutil.GVKForObject(obj, c.Scheme()) if err != nil { return errors.Wrapf(err, "failed to mark %s as ok to delete: failed to get GVK for object", klog.KObj(obj)) } - patchHelper, err := patch.NewHelper(obj, c) - if err != nil { - return errors.Wrapf(err, "failed to mark %s %s as ok to delete", gvk.Kind, klog.KObj(obj)) - } + orig := obj.DeepCopyObject().(client.Object) annotations := obj.GetAnnotations() if annotations == nil { @@ -133,7 +159,12 @@ func MarkAsOkToDelete(ctx context.Context, c client.Client, obj client.Object) e annotations[runtimev1.OkToDeleteAnnotation] = "" obj.SetAnnotations(annotations) - if err := patchHelper.Patch(ctx, obj); err != nil { + // In some cases it is preferred to not update resourceVersion in the input object, + // because this could lead to conflict errors e.g. when patching at the end of a reconcile loop. + if !updateResourceVersionOnObject { + obj = obj.DeepCopyObject().(client.Object) + } + if err := c.Patch(ctx, obj, client.MergeFrom(orig)); err != nil { return errors.Wrapf(err, "failed to mark %s %s as ok to delete", gvk.Kind, klog.KObj(obj)) } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/runtime/client/client.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/runtime/client/client.go index 1d260e7450..b9aa2fd4bd 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/runtime/client/client.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/runtime/client/client.go @@ -20,6 +20,7 @@ package client import ( "bytes" "context" + "crypto/tls" "encoding/json" "fmt" "io" @@ -32,6 +33,7 @@ import ( "strings" "time" + "github.com/go-logr/logr" "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -41,9 +43,12 @@ import ( utilnet "k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/client-go/transport" + "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/certwatcher" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" runtimev1 "sigs.k8s.io/cluster-api/api/runtime/v1beta2" @@ -52,6 +57,7 @@ import ( runtimemetrics "sigs.k8s.io/cluster-api/internal/runtime/metrics" runtimeregistry "sigs.k8s.io/cluster-api/internal/runtime/registry" "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/cache" ) type errCallingExtensionHandler error @@ -68,24 +74,67 @@ type Options struct { } // New returns a new Client. -func New(options Options) runtimeclient.Client { - return &client{ - certFile: options.CertFile, - keyFile: options.KeyFile, - catalog: options.Catalog, - registry: options.Registry, - client: options.Client, +func New(options Options) (runtimeclient.Client, *certwatcher.CertWatcher, error) { + httpClientCache := cache.New[httpClientEntry](24 * time.Hour) + + var certWatcher *certwatcher.CertWatcher + if options.CertFile != "" && options.KeyFile != "" { + var err error + certWatcher, err = certwatcher.New(options.CertFile, options.KeyFile) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to create RuntimeSDK client: failed to create cert-watcher") + } + certWatcher.RegisterCallback(func(_ tls.Certificate) { + httpClientCache.DeleteAll() + }) } + return &client{ + certFile: options.CertFile, + keyFile: options.KeyFile, + catalog: options.Catalog, + registry: options.Registry, + client: options.Client, + httpClientsCache: httpClientCache, + }, certWatcher, nil } var _ runtimeclient.Client = &client{} type client struct { - certFile string - keyFile string - catalog *runtimecatalog.Catalog - registry runtimeregistry.ExtensionRegistry - client ctrlclient.Client + certFile string + keyFile string + catalog *runtimecatalog.Catalog + registry runtimeregistry.ExtensionRegistry + client ctrlclient.Client + httpClientsCache cache.Cache[httpClientEntry] +} + +type httpClientEntry struct { + // Note: caData and hostName are the variable parts in the TLSConfig + // for an http.Client that is used to call runtime extensions. + caData []byte + hostName string + + client *http.Client +} + +func newHTTPClientEntry(hostName string, caData []byte, client *http.Client) httpClientEntry { + return httpClientEntry{ + hostName: hostName, + caData: caData, + client: client, + } +} + +func newHTTPClientEntryKey(hostName string, caData []byte) string { + return httpClientEntry{ + hostName: hostName, + caData: caData, + }.Key() +} + +func (r httpClientEntry) Key() string { + return fmt.Sprintf("%s/%s", r.hostName, string(r.caData)) } func (c *client) WarmUp(extensionConfigList *runtimev1.ExtensionConfigList) error { @@ -105,26 +154,28 @@ func (c *client) Discover(ctx context.Context, extensionConfig *runtimev1.Extens return nil, errors.Wrapf(err, "failed to discover extension %q: failed to compute GVH of hook", extensionConfig.Name) } + httpClient, err := c.getHTTPClient(extensionConfig.Spec.ClientConfig) + if err != nil { + return nil, errors.Wrapf(err, "failed to discover extension %q: failed to get http client", extensionConfig.Name) + } + request := &runtimehooksv1.DiscoveryRequest{} response := &runtimehooksv1.DiscoveryResponse{} opts := &httpCallOptions{ - certFile: c.certFile, - keyFile: c.keyFile, catalog: c.catalog, config: extensionConfig.Spec.ClientConfig, registrationGVH: hookGVH, hookGVH: hookGVH, timeout: defaultDiscoveryTimeout, + httpClient: httpClient, } if err := httpCall(ctx, request, response, opts); err != nil { return nil, errors.Wrapf(err, "failed to discover extension %q", extensionConfig.Name) } - // Check to see if the response is a failure and handle the failure accordingly. - if response.GetStatus() == runtimehooksv1.ResponseStatusFailure { - log.Info(fmt.Sprintf("Failed to discover extension %q: got failure response with message %v", extensionConfig.Name, response.GetMessage())) - // Don't add the message to the error as it is may be unique causing too many reconciliations. Ref: https://github.com/kubernetes-sigs/cluster-api/issues/6921 - return nil, errors.Errorf("failed to discover extension %q: got failure response, please check controller logs for errors", extensionConfig.Name) + // Check to see if the response is not a success and handle the failure accordingly. + if err := validateResponseStatus(log, response, "discover extension", extensionConfig.Name); err != nil { + return nil, err } // Check to see if the response is valid. @@ -172,12 +223,49 @@ func (c *client) Unregister(extensionConfig *runtimev1.ExtensionConfig) error { return nil } +func (c *client) GetAllExtensions(ctx context.Context, hook runtimecatalog.Hook, forObject ctrlclient.Object) ([]string, error) { + hookName := runtimecatalog.HookName(hook) + log := ctrl.LoggerFrom(ctx).WithValues("hook", hookName) + ctx = ctrl.LoggerInto(ctx, log) + gvh, err := c.catalog.GroupVersionHook(hook) + if err != nil { + return nil, errors.Wrapf(err, "failed to get extension handlers for hook %q: failed to compute GroupVersionHook", hookName) + } + forObjectGVK, err := apiutil.GVKForObject(forObject, c.client.Scheme()) + if err != nil { + return nil, errors.Wrapf(err, "failed to get extension handlers for hook %q: failed to get GroupVersionKind for the object the hook is executed for", hookName) + } + + registrations, err := c.registry.List(gvh.GroupHook()) + if err != nil { + return nil, errors.Wrapf(err, "failed to get extension handlers for hook %q", gvh.GroupHook()) + } + + log.V(4).Info(fmt.Sprintf("Getting all extensions of hook %q for %s %s", hookName, forObjectGVK.Kind, klog.KObj(forObject))) + matchingRegistrations := []string{} + for _, registration := range registrations { + // Compute whether the object the get is being made for matches the namespaceSelector + namespaceMatches, err := c.matchNamespace(ctx, registration.NamespaceSelector, forObject.GetNamespace()) + if err != nil { + return nil, errors.Wrapf(err, "failed to get extension handlers for hook %q: failed to get extension handler %q", gvh.GroupHook(), registration.Name) + } + // If the object namespace isn't matched by the registration NamespaceSelector don't return it. + if !namespaceMatches { + log.V(5).Info(fmt.Sprintf("skipping extension handler %q as object '%s/%s' does not match selector %q of ExtensionConfig", registration.Name, forObject.GetNamespace(), forObject.GetName(), registration.NamespaceSelector)) + continue + } + matchingRegistrations = append(matchingRegistrations, registration.Name) + } + + return matchingRegistrations, nil +} + // CallAllExtensions calls all the ExtensionHandlers registered for the hook. // The ExtensionHandlers are called sequentially. The function exits immediately after any of the ExtensionHandlers return an error. // This ensures we don't end up waiting for timeout from multiple unreachable Extensions. // See CallExtension for more details on when an ExtensionHandler returns an error. // The aggregated result of the ExtensionHandlers is updated into the response object passed to the function. -func (c *client) CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject) error { +func (c *client) CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook, forObject ctrlclient.Object, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject) error { hookName := runtimecatalog.HookName(hook) log := ctrl.LoggerFrom(ctx).WithValues("hook", hookName) ctx = ctrl.LoggerInto(ctx, log) @@ -194,33 +282,22 @@ func (c *client) CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook return errors.Wrapf(err, "failed to call extension handlers for hook %q: response object is invalid for hook", gvh.GroupHook()) } - registrations, err := c.registry.List(gvh.GroupHook()) + // Get all matching extension handlers for this hook and object. + matchingHandlers, err := c.GetAllExtensions(ctx, hook, forObject) if err != nil { return errors.Wrapf(err, "failed to call extension handlers for hook %q", gvh.GroupHook()) } - log.V(4).Info(fmt.Sprintf("Calling all extensions of hook %q", hookName)) responses := []runtimehooksv1.ResponseObject{} - for _, registration := range registrations { + for _, handlerName := range matchingHandlers { // Creates a new instance of the response parameter. responseObject, err := c.catalog.NewResponse(gvh) if err != nil { - return errors.Wrapf(err, "failed to call extension handlers for hook %q: failed to call extension handler %q", gvh.GroupHook(), registration.Name) + return errors.Wrapf(err, "failed to call extension handlers for hook %q: failed to call extension handler %q", gvh.GroupHook(), handlerName) } tmpResponse := responseObject.(runtimehooksv1.ResponseObject) - // Compute whether the object the call is being made for matches the namespaceSelector - namespaceMatches, err := c.matchNamespace(ctx, registration.NamespaceSelector, forObject.GetNamespace()) - if err != nil { - return errors.Wrapf(err, "failed to call extension handlers for hook %q: failed to call extension handler %q", gvh.GroupHook(), registration.Name) - } - // If the object namespace isn't matched by the registration NamespaceSelector skip the call. - if !namespaceMatches { - log.V(5).Info(fmt.Sprintf("skipping extension handler %q as object '%s/%s' does not match selector %q of ExtensionConfig", registration.Name, forObject.GetNamespace(), forObject.GetName(), registration.NamespaceSelector)) - continue - } - - err = c.CallExtension(ctx, hook, forObject, registration.Name, request, tmpResponse) + err = c.CallExtension(ctx, hook, forObject, handlerName, request, tmpResponse) // If one of the extension handlers fails lets short-circuit here and return early. if err != nil { log.Error(err, "failed to call extension handlers") @@ -271,7 +348,7 @@ func aggregateSuccessfulResponses(aggregatedResponse runtimehooksv1.ResponseObje // Nb. FailurePolicy does not affect the following kinds of errors: // - Internal errors. Examples: hooks is incompatible with ExtensionHandler, ExtensionHandler information is missing. // - Error when ExtensionHandler returns a response with `Status` set to `Failure`. -func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, name string, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject, opts ...runtimeclient.CallExtensionOption) error { +func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, forObject ctrlclient.Object, name string, request runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject, opts ...runtimeclient.CallExtensionOption) error { // Calculate the options. options := &runtimeclient.CallExtensionOptions{} for _, opt := range opts { @@ -336,15 +413,19 @@ func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, fo } } + httpClient, err := c.getHTTPClient(registration.ClientConfig) + if err != nil { + return errors.Wrapf(err, "failed to call extension handler %q: failed to get http client", name) + } + httpOpts := &httpCallOptions{ - certFile: c.certFile, - keyFile: c.keyFile, catalog: c.catalog, config: registration.ClientConfig, registrationGVH: registration.GroupVersionHook, hookGVH: hookGVH, name: strings.TrimSuffix(registration.Name, "."+registration.ExtensionConfigName), timeout: timeoutDuration, + httpClient: httpClient, } err = httpCall(ctx, request, response, httpOpts) if err != nil { @@ -362,15 +443,13 @@ func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, fo return errors.Wrapf(err, "failed to call extension handler %q", name) } - // If the received response is a failure then return an error. - if response.GetStatus() == runtimehooksv1.ResponseStatusFailure { - log.Info(fmt.Sprintf("Failed to call extension handler %q: got failure response with message %v", name, response.GetMessage())) - // Don't add the message to the error as it is may be unique causing too many reconciliations. Ref: https://github.com/kubernetes-sigs/cluster-api/issues/6921 - return errors.Errorf("failed to call extension handler %q: got failure response, please check controller logs for errors", name) + // If the received response is not a success then return an error. + if err := validateResponseStatus(log, response, "call extension handler", name); err != nil { + return err } if retryResponse, ok := response.(runtimehooksv1.RetryResponseObject); ok && retryResponse.GetRetryAfterSeconds() != 0 { - log.Info(fmt.Sprintf("Extension handler returned blocking response with retryAfterSeconds of %d", retryResponse.GetRetryAfterSeconds())) + log.V(4).Info(fmt.Sprintf("Extension handler returned blocking response with retryAfterSeconds of %d", retryResponse.GetRetryAfterSeconds())) } else { log.V(4).Info("Extension handler returned success response") } @@ -388,6 +467,48 @@ func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, fo return nil } +func (c *client) getHTTPClient(config runtimev1.ClientConfig) (*http.Client, error) { + // Note: we are passing an empty gvh and "" as name because the only relevant part of the url + // for this function is the Hostname, which derives from config (ghv and name are appended to the path). + extensionURL, err := urlForExtension(config, runtimecatalog.GroupVersionHook{}, "") + if err != nil { + return nil, err + } + + if cacheEntry, ok := c.httpClientsCache.Has(newHTTPClientEntryKey(extensionURL.Hostname(), config.CABundle)); ok { + return cacheEntry.client, nil + } + + httpClient, err := createHTTPClient(c.certFile, c.keyFile, config.CABundle, extensionURL.Hostname()) + if err != nil { + return nil, err + } + + c.httpClientsCache.Add(newHTTPClientEntry(extensionURL.Hostname(), config.CABundle, httpClient)) + return httpClient, nil +} + +func createHTTPClient(certFile, keyFile string, caData []byte, hostName string) (*http.Client, error) { + httpClient := &http.Client{} + tlsConfig, err := transport.TLSConfigFor(&transport.Config{ + TLS: transport.TLSConfig{ + CertFile: certFile, + KeyFile: keyFile, + CAData: caData, + ServerName: hostName, + }, + }) + if err != nil { + return nil, errors.Wrap(err, "failed to create tls config") + } + + // This also adds http2 + httpClient.Transport = utilnet.SetTransportDefaults(&http.Transport{ + TLSClientConfig: tlsConfig, + }) + return httpClient, nil +} + // cloneAndAddSettings creates a new request object and adds settings to it. func cloneAndAddSettings(request runtimehooksv1.RequestObject, registrationSettings map[string]string) runtimehooksv1.RequestObject { // Merge the settings from registration with the settings in the request. @@ -406,14 +527,13 @@ func cloneAndAddSettings(request runtimehooksv1.RequestObject, registrationSetti } type httpCallOptions struct { - certFile string - keyFile string catalog *runtimecatalog.Catalog config runtimev1.ClientConfig registrationGVH runtimecatalog.GroupVersionHook hookGVH runtimecatalog.GroupVersionHook name string timeout time.Duration + httpClient *http.Client } func httpCall(ctx context.Context, request, response runtime.Object, opts *httpCallOptions) error { @@ -492,25 +612,8 @@ func httpCall(ctx context.Context, request, response runtime.Object, opts *httpC return errors.Wrap(err, "http call failed: failed to create http request") } - // Use client-go's transport.TLSConfigureFor to ensure good defaults for tls - client := http.DefaultClient - tlsConfig, err := transport.TLSConfigFor(&transport.Config{ - TLS: transport.TLSConfig{ - CertFile: opts.certFile, - KeyFile: opts.keyFile, - CAData: opts.config.CABundle, - ServerName: extensionURL.Hostname(), - }, - }) - if err != nil { - return errors.Wrap(err, "http call failed: failed to create tls config") - } - // This also adds http2 - client.Transport = utilnet.SetTransportDefaults(&http.Transport{ - TLSClientConfig: tlsConfig, - }) - - resp, err := client.Do(httpRequest) + // Call the extension. + resp, err := opts.httpClient.Do(httpRequest) // Create http request metric. defer func() { @@ -695,3 +798,19 @@ func ExtensionNameFromHandlerName(registeredHandlerName string) (string, error) } return parts[1], nil } + +// validateResponseStatus checks if the response status is successful and returns an error otherwise. +// It logs appropriate messages for failure and unknown statuses. +func validateResponseStatus(log logr.Logger, response runtimehooksv1.ResponseObject, operationName, targetName string) error { + if response.GetStatus() != runtimehooksv1.ResponseStatusSuccess { + if response.GetStatus() == runtimehooksv1.ResponseStatusFailure { + log.Info(fmt.Sprintf("Failed to %s %q: got failure response with message %v", operationName, targetName, response.GetMessage())) + // Don't add the message to the error as it is may be unique causing too many reconciliations. Ref: https://github.com/kubernetes-sigs/cluster-api/issues/6921 + return errors.Errorf("failed to %s %q: got failure response, please check controller logs for errors", operationName, targetName) + } + // Handle unknown status. + log.Info(fmt.Sprintf("Failed to %s %q: got unknown response status %q with message %v", operationName, targetName, response.GetStatus(), response.GetMessage())) + return errors.Errorf("failed to %s %q: got unknown response status %q, please check controller logs for errors", operationName, targetName, response.GetStatus()) + } + return nil +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/client/client.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/client/client.go new file mode 100644 index 0000000000..aa07dd823f --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/client/client.go @@ -0,0 +1,295 @@ +/* +Copyright 2025 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 client provides utils for usage with the controller-runtime client. +package client + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/pkg/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + kerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" +) + +var ( + // waitBackoff is the timeout used when waiting for the cache to become up-to-date. + // This adds up to ~ 10 seconds max wait duration. + waitBackoff = wait.Backoff{ + Duration: 25 * time.Microsecond, + Cap: 2 * time.Second, + Factor: 1.2, + Steps: 63, + } +) + +// WaitForCacheToBeUpToDate waits until the cache is up-to-date in the sense of that the cache contains +// all passed in objects with at least the passed in resourceVersion. +// This is done by retrieving objects from the cache via the client and then comparing resourceVersions. +// Note: This func will update the passed in objects while polling. +// Note: resourceVersion must be set on the passed in objects. +// Note: The generic parameter enforces that all objects have the same type. +func WaitForCacheToBeUpToDate[T client.Object](ctx context.Context, c client.Client, action string, objs ...T) error { + return waitFor(ctx, c, action, checkIfObjectUpToDate, objs...) +} + +// WaitForObjectsToBeAddedToTheCache waits until the cache is up-to-date in the sense of that the +// passed in objects exist in the cache. +// Note: This func will update the passed in objects while polling. +// Note: The generic parameter enforces that all objects have the same type. +func WaitForObjectsToBeAddedToTheCache[T client.Object](ctx context.Context, c client.Client, action string, objs ...T) error { + return waitFor(ctx, c, action, checkIfObjectAdded, objs...) +} + +// WaitForObjectsToBeDeletedFromTheCache waits until the cache is up-to-date in the sense of that the +// passed in objects have been either removed from the cache or they have a deletionTimestamp set. +// Note: This func will update the passed in objects while polling. +// Note: The generic parameter enforces that all objects have the same type. +func WaitForObjectsToBeDeletedFromTheCache[T client.Object](ctx context.Context, c client.Client, action string, objs ...T) error { + return waitFor(ctx, c, action, checkIfObjectDeleted, objs...) +} + +// checkIfObjectUpToDate checks if an object is up-to-date and returns an error if it is not. +func checkIfObjectUpToDate(ctx context.Context, c client.Client, desiredObj desiredObject) (isErrorRetryable bool, err error) { + if desiredObj.MinimumResourceVersion == "" { + // Unexpected error occurred: resourceVersion not set on passed in object (not retryable). + return false, errors.Errorf("%s: cannot compare with invalid resourceVersion: resourceVersion not set", + klog.KObj(desiredObj.Object)) + } + + if err := c.Get(ctx, desiredObj.Key, desiredObj.Object); err != nil { + if apierrors.IsNotFound(err) { + // Done, object was deleted in the meantime. + return false, nil + } + // Unexpected error occurred (not retryable). + return false, err + } + + cmp, err := compareResourceVersion(desiredObj.Object.GetResourceVersion(), desiredObj.MinimumResourceVersion) + if err != nil { + // Unexpected error occurred: invalid resourceVersion (not retryable). + return false, errors.Wrapf(err, "%s: cannot compare with invalid resourceVersion: current: %s, expected to be >= %s", + klog.KObj(desiredObj.Object), desiredObj.Object.GetResourceVersion(), desiredObj.MinimumResourceVersion) + } + if cmp < 0 { + // resourceVersion < MinimumResourceVersion (retryable). + return true, errors.Errorf("%s: resourceVersion not yet up-to-date: current: %s, expected to be >= %s", + klog.KObj(desiredObj.Object), desiredObj.Object.GetResourceVersion(), desiredObj.MinimumResourceVersion) + } + + // Done, resourceVersion is new enough. + return false, nil +} + +func checkIfObjectAdded(ctx context.Context, c client.Client, desiredObj desiredObject) (isErrorRetryable bool, err error) { + if err := c.Get(ctx, desiredObj.Key, desiredObj.Object); err != nil { + if apierrors.IsNotFound(err) { + // Object is not yet in the cache (retryable). + return true, err + } + // Unexpected error occurred (not retryable). + return false, err + } + + // Done, object exists in the cache. + return false, nil +} + +func checkIfObjectDeleted(ctx context.Context, c client.Client, desiredObj desiredObject) (isErrorRetryable bool, err error) { + if err := c.Get(ctx, desiredObj.Key, desiredObj.Object); err != nil { + if apierrors.IsNotFound(err) { + // Done, object has been removed from the cache. + return false, nil + } + // Unexpected error occurred (not retryable). + return false, err + } + + if !desiredObj.Object.GetDeletionTimestamp().IsZero() { + // Done, object has deletionTimestamp set. + return false, nil + } + + // Object does not have deletionTimestamp set yet (retryable). + return true, fmt.Errorf("%s still exists", klog.KObj(desiredObj.Object)) +} + +type desiredObject struct { + Object client.Object + Key client.ObjectKey + MinimumResourceVersion string +} + +type checkFunc func(ctx context.Context, c client.Client, desiredObj desiredObject) (retryableErr bool, err error) + +func waitFor[T client.Object](ctx context.Context, c client.Client, action string, checkFunc checkFunc, objs ...T) error { + // Done, if there are no objects. + if len(objs) == 0 { + return nil + } + + var o any = objs[0] + if _, ok := o.(*unstructured.Unstructured); ok { + return errors.Errorf("failed to wait for up-to-date objects in the cache after %s: Unstructured is not supported", action) + } + + // All objects have the same type, so we can just take the GVK of the first object. + objGVK, err := apiutil.GVKForObject(objs[0], c.Scheme()) + if err != nil { + return errors.Wrapf(err, "failed to wait for up-to-date objects in the cache after %s", action) + } + + log := ctrl.LoggerFrom(ctx) + + desiredObjects := make([]desiredObject, len(objs)) + for i, obj := range objs { + desiredObjects[i] = desiredObject{ + Object: obj, + Key: client.ObjectKeyFromObject(obj), + MinimumResourceVersion: obj.GetResourceVersion(), + } + } + + now := time.Now() + + var pollErrs []error + err = wait.ExponentialBackoffWithContext(ctx, waitBackoff, func(ctx context.Context) (bool, error) { + pollErrs = nil + + for _, desiredObj := range desiredObjects { + if isErrorRetryable, err := checkFunc(ctx, c, desiredObj); err != nil { + pollErrs = append(pollErrs, err) + if !isErrorRetryable { + // Stop polling, non-retryable error occurred. + return true, nil + } + } + } + + if len(pollErrs) > 0 { + // Continue polling, only retryable errors occurred. + return false, nil + } + + // Stop polling, all objects are up-to-date. + return true, nil + }) + + waitDuration := time.Since(now) + + if err != nil || len(pollErrs) > 0 { + waitDurationMetric.WithLabelValues(objGVK.Kind, "error").Observe(waitDuration.Seconds()) + + var errSuffix string + if err != nil { + if wait.Interrupted(err) { + errSuffix = ": timed out" + } else { + errSuffix = fmt.Sprintf(": %s", err.Error()) + } + } + err := errors.Errorf("failed to wait for up-to-date %s objects in the cache after %s%s: %s", objGVK.Kind, action, errSuffix, kerrors.NewAggregate(pollErrs)) + log.Error(err, "Failed to wait for cache to be up-to-date", "kind", objGVK.Kind, "waitDuration", waitDuration) + return err + } + + waitDurationMetric.WithLabelValues(objGVK.Kind, "success").Observe(waitDuration.Seconds()) + + // Log on a high log-level if it took a long time for the cache to be up-to-date. + if waitDuration >= 1*time.Second { + log.Info("Successfully waited for cache to be up-to-date (>=1s)", "kind", objGVK.Kind, "waitDuration", waitDuration) + } else { + log.V(10).Info("Successfully waited for cache to be up-to-date", "kind", objGVK.Kind, "waitDuration", waitDuration) + } + + return nil +} + +type invalidResourceVersion struct { + rv string +} + +func (i invalidResourceVersion) Error() string { + return fmt.Sprintf("resource version is not well formed: %s", i.rv) +} + +// compareResourceVersion runs a comparison between two ResourceVersions. This +// only has semantic meaning when the comparison is done on two objects of the +// same resource. The return values are: +// +// -1: If RV a < RV b +// 0: If RV a == RV b +// +1: If RV a > RV b +// +// The function will return an error if the resource version is not a properly +// formatted positive integer, but has no restriction on length. A properly +// formatted integer will not contain leading zeros or non integer characters. +// Zero is also considered an invalid value as it is used as a special value in +// list/watch events and will never be a live resource version. +// TODO(controller-runtime-0.23): This code has been copied from +// https://github.com/kubernetes/kubernetes/blob/v1.35.0-alpha.2/staging/src/k8s.io/apimachinery/pkg/util/resourceversion/resourceversion.go +// and will be removed once we bump to CR v0.23 / k8s.io/apimachinery v1.35.0. +func compareResourceVersion(a, b string) (int, error) { + if !isWellFormed(a) { + return 0, invalidResourceVersion{rv: a} + } + if !isWellFormed(b) { + return 0, invalidResourceVersion{rv: b} + } + // both are well-formed integer strings with no leading zeros + aLen := len(a) + bLen := len(b) + switch { + case aLen < bLen: + // shorter is less + return -1, nil + case aLen > bLen: + // longer is greater + return 1, nil + default: + // equal-length compares lexically + return strings.Compare(a, b), nil + } +} + +func isWellFormed(s string) bool { + if len(s) == 0 { //nolint:gocritic // not going to modify code copied from upstream + return false + } + if s[0] == '0' { + return false + } + for i := range s { + if !isDigit(s[i]) { + return false + } + } + return true +} + +func isDigit(b byte) bool { + return b >= '0' && b <= '9' +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/client/metrics.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/client/metrics.go new file mode 100644 index 0000000000..cb8c7c3e27 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/client/metrics.go @@ -0,0 +1,35 @@ +/* +Copyright 2023 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 client + +import ( + "github.com/prometheus/client_golang/prometheus" + ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics" +) + +func init() { + // Register the metrics at the controller-runtime metrics registry. + ctrlmetrics.Registry.MustRegister(waitDurationMetric) +} + +var ( + waitDurationMetric = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "capi_client_cache_wait_duration_seconds", + Help: "Duration that we waited for the cache to be up-to-date in seconds, broken down by kind and status", + Buckets: []float64{0.00001, 0.000025, 0.00005, 0.0001, 0.00025, 0.0005, 0.001, 0.005, 0.025, 0.05, 0.1, 0.2, 0.4, 0.6, 0.8, 1.0, 2.5, 5, 10}, + }, []string{"kind", "status"}) +) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/builder.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/builder.go new file mode 100644 index 0000000000..7d6de40f3d --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/builder.go @@ -0,0 +1,322 @@ +/* +Copyright 2025 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 controller + +import ( + "errors" + "math" + "strings" + "sync" + "time" + + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + + "sigs.k8s.io/cluster-api/feature" + "sigs.k8s.io/cluster-api/util/cache" + predicatesutil "sigs.k8s.io/cluster-api/util/predicates" +) + +const ( + // defaultReconciliationTimeout is the default ReconciliationTimeout that is set. + // This means that the context of a Reconcile will time out after 1m. + defaultReconciliationTimeout = 1 * time.Minute +) + +// Builder is a wrapper around controller-runtime's builder.Builder. +type Builder struct { + builder *builder.Builder + mgr manager.Manager + predicateLog logr.Logger + options controller.TypedOptions[reconcile.Request] + forObject client.Object + controllerName string + rateLimitInterval time.Duration +} + +// NewControllerManagedBy returns a new controller builder that will be started by the provided Manager. +func NewControllerManagedBy(m manager.Manager, predicateLog logr.Logger) *Builder { + return &Builder{ + builder: builder.ControllerManagedBy(m), + mgr: m, + predicateLog: predicateLog, + } +} + +// For defines the type of Object being *reconciled*, and configures the ControllerManagedBy to respond to create / delete / +// update events by *reconciling the object*. +func (blder *Builder) For(object client.Object, opts ...builder.ForOption) *Builder { + blder.forObject = object + blder.builder.For(object, opts...) + return blder +} + +// Owns defines types of Objects being *generated* by the ControllerManagedBy, and configures the ControllerManagedBy to respond to +// create / delete / update events by *reconciling the owner object*. +func (blder *Builder) Owns(object client.Object, predicates ...predicate.Predicate) *Builder { + // Note: Prepend a ResourceIsChanged predicate to all "secondary" watches, this will filter out resync events, + // because resync from the primary object is enough. + // Note: Prepending it to avoid calling (potentially) more resource intensive predicates for resyncs. + predicates = append([]predicate.Predicate{predicatesutil.ResourceIsChanged(blder.mgr.GetScheme(), blder.predicateLog)}, predicates...) + blder.builder.Owns(object, builder.WithPredicates(predicates...)) + return blder +} + +// Watches defines the type of Object to watch, and configures the ControllerManagedBy to respond to create / delete / +// update events by *reconciling the object* with the given EventHandler. +func (blder *Builder) Watches(object client.Object, eventHandler handler.TypedEventHandler[client.Object, reconcile.Request], predicates ...predicate.Predicate) *Builder { + // Note: Prepend a ResourceIsChanged predicate to all "secondary" watches, this will filter out resync events, + // because resync from the primary object is enough. + // Note: Prepending it to avoid calling (potentially) more resource intensive predicates for resyncs. + predicates = append([]predicate.Predicate{predicatesutil.ResourceIsChanged(blder.mgr.GetScheme(), blder.predicateLog)}, predicates...) + blder.builder.Watches(object, eventHandler, builder.WithPredicates(predicates...)) + return blder +} + +// WatchesMetadata is the same as Watches, but forces the internal cache to only watch PartialObjectMetadata. +func (blder *Builder) WatchesMetadata(object client.Object, eventHandler handler.TypedEventHandler[client.Object, reconcile.Request], + predicates ...predicate.Predicate) *Builder { + // Note: Prepend a ResourceIsChanged predicate to all "secondary" watches, this will filter out resync events, + // because resync from the primary object is enough. + // Note: Prepending it to avoid calling (potentially) more resource intensive predicates for resyncs. + predicates = append([]predicate.Predicate{predicatesutil.ResourceIsChanged(blder.mgr.GetScheme(), blder.predicateLog)}, predicates...) + blder.builder.Watches(object, eventHandler, builder.WithPredicates(predicates...), builder.OnlyMetadata) + return blder +} + +// WatchesRawSource exposes the lower-level ControllerManagedBy Watches functions through the builder. +func (blder *Builder) WatchesRawSource(src source.TypedSource[reconcile.Request]) *Builder { + blder.builder.WatchesRawSource(src) + return blder +} + +// WithOptions overrides the controller options used in doController. Defaults to empty. +func (blder *Builder) WithOptions(options controller.TypedOptions[reconcile.Request]) *Builder { + blder.options = options + return blder +} + +// WithRateLimitInterval overrides the default rate limit interval, 1s. +// Note: intervals lower than 1s will be ignored. +func (blder *Builder) WithRateLimitInterval(interval time.Duration) *Builder { + blder.rateLimitInterval = interval + return blder +} + +// WithEventFilter sets the event filters, to filter which create/update/delete/generic events eventually +// trigger reconciliations. For example, filtering on whether the resource version has changed. +func (blder *Builder) WithEventFilter(p predicate.Predicate) *Builder { + blder.builder.WithEventFilter(p) + return blder +} + +// Named sets the name of the controller to the given name. The name shows up +// in metrics, among other things, and thus should be a prometheus compatible name +// (underscores and alphanumeric characters only). +func (blder *Builder) Named(name string) *Builder { + blder.controllerName = name + blder.builder.Named(name) + return blder +} + +// Controller is the controller-runtime Controller interface with +// additional methods to defer the next reconcile for a request / object. +type Controller interface { + controller.Controller + DeferNextReconcile(req reconcile.Request, reconcileAfter time.Time) + DeferNextReconcileForObject(obj metav1.Object, reconcileAfter time.Time) +} + +// Complete builds the Application Controller. +func (blder *Builder) Complete(r reconcile.TypedReconciler[reconcile.Request]) error { + _, err := blder.Build(r) + return err +} + +// Build builds the Application Controller and returns the Controller it created. +func (blder *Builder) Build(r reconcile.TypedReconciler[reconcile.Request]) (Controller, error) { + if feature.Gates.Enabled(feature.ReconcilerRateLimiting) && !feature.Gates.Enabled(feature.PriorityQueue) { + return nil, errors.New("if feature gate ReconcilerRateLimiting is enabled, feature gate PriorityQueue must be enabled as well") + } + + // Get GVK of the for object. + var gvk schema.GroupVersionKind + hasGVK := blder.forObject != nil + if hasGVK { + var err error + gvk, err = apiutil.GVKForObject(blder.forObject, blder.mgr.GetScheme()) + if err != nil { + return nil, err + } + } + + // Get controllerName. + controllerName := blder.controllerName + if controllerName == "" { + controllerName = strings.ToLower(gvk.Kind) + } + + // Default ReconciliationTimeout. + // This means that the context of a Reconcile will time out after 1m. + if blder.options.ReconciliationTimeout == 0 { + blder.options.ReconciliationTimeout = defaultReconciliationTimeout + } + + // Default LogConstructor. + // This overwrites the LogConstructor defaulting in controller-runtime, because we do not want + // to add additional & redundant name & namespace k/v pairs like controller-runtime. + if blder.options.LogConstructor == nil { + log := blder.mgr.GetLogger().WithValues( + "controller", controllerName, + ) + if hasGVK { + log = log.WithValues( + "controllerGroup", gvk.Group, + "controllerKind", gvk.Kind, + ) + } + + blder.options.LogConstructor = func(req *reconcile.Request) logr.Logger { + // Note: This logic has to be inside the LogConstructor as this k/v pair + // is different for every single reconcile.Request + log := log + if req != nil { + if hasGVK { + log = log.WithValues(gvk.Kind, klog.KRef(req.Namespace, req.Name)) + } + // Note: Not setting additional name & namespace k/v pairs like controller-runtime + // as they are redundant. + } + return log + } + } + + // Sets the rate limit interval. + rateLimitInterval := time.Second + if blder.rateLimitInterval > time.Second { + rateLimitInterval = blder.rateLimitInterval + } + + var queueRateLimiter *typedItemExponentialFailureRateLimiter[reconcile.Request] + if feature.Gates.Enabled(feature.ReconcilerRateLimiting) { + queueRateLimiter = newTypedItemExponentialFailureRateLimiter[reconcile.Request](rateLimitInterval, 5*time.Millisecond, 1000*time.Second) + blder.options.RateLimiter = queueRateLimiter + } + + // Passing the options to the underlying builder here because we modified them above. + blder.builder.WithOptions(blder.options) + + // Create reconcileCache. + reconcileCache := cache.New[reconcileCacheEntry](cache.DefaultTTL) + + c, err := blder.builder.Build(&reconcilerWrapper{ + name: controllerName, + reconciler: r, + reconcileCache: reconcileCache, + rateLimitInterval: rateLimitInterval, + queueRateLimiter: queueRateLimiter, + }) + if err != nil { + return nil, err + } + + // Initialize metrics to align to what controller-runtime is doing for its metrics. + // Note: This is not done for reconcileTime because we cannot add data to this metric + // without skewing the data. + reconcileTotal.WithLabelValues(controllerName, labelError).Add(0) + reconcileTotal.WithLabelValues(controllerName, labelRequeueAfter).Add(0) + reconcileTotal.WithLabelValues(controllerName, labelRequeue).Add(0) + reconcileTotal.WithLabelValues(controllerName, labelSuccess).Add(0) + + return &controllerWrapper{ + TypedController: c, + reconcileCache: reconcileCache, + }, nil +} + +func newTypedItemExponentialFailureRateLimiter[T comparable](rateLimitInterval time.Duration, baseDelay time.Duration, maxDelay time.Duration) *typedItemExponentialFailureRateLimiter[T] { + return &typedItemExponentialFailureRateLimiter[T]{ + failures: map[T]int{}, + rateLimitInterval: rateLimitInterval, + baseDelay: baseDelay, + maxDelay: maxDelay, + } +} + +// typedItemExponentialFailureRateLimiter does a simple baseDelay*2^ limit +// dealing with max failures and expiration are up to the caller. +// Note: In addition to the upstream TypedItemExponentialFailureRateLimiter it adds +// rateLimitInterval to the backoff duration and it changes the behavior of the Forget method. +// The Forget method is now a no-op and the ActualForget must be used to forget an item. +// This is done to ensure the reconcileWrapper has full control over forget and the forget calls +// in controller-runtime are no-ops. +type typedItemExponentialFailureRateLimiter[T comparable] struct { + failuresLock sync.Mutex + failures map[T]int + + rateLimitInterval time.Duration + baseDelay time.Duration + maxDelay time.Duration +} + +func (r *typedItemExponentialFailureRateLimiter[T]) When(item T) time.Duration { + r.failuresLock.Lock() + defer r.failuresLock.Unlock() + + exp := r.failures[item] + r.failures[item]++ + + // The backoff is capped such that 'calculated' value never overflows. + backoff := float64(r.rateLimitInterval) + float64(r.baseDelay.Nanoseconds())*math.Pow(2, float64(exp)) + if backoff > math.MaxInt64 { + return r.maxDelay + } + + calculated := time.Duration(backoff) + if calculated > r.maxDelay { + return r.maxDelay + } + + return calculated +} + +func (r *typedItemExponentialFailureRateLimiter[T]) NumRequeues(item T) int { + r.failuresLock.Lock() + defer r.failuresLock.Unlock() + + return r.failures[item] +} + +func (r *typedItemExponentialFailureRateLimiter[T]) Forget(_ T) {} + +func (r *typedItemExponentialFailureRateLimiter[T]) ActualForget(item T) { + r.failuresLock.Lock() + defer r.failuresLock.Unlock() + + delete(r.failures, item) +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/controller.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/controller.go new file mode 100644 index 0000000000..a190197059 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/controller.go @@ -0,0 +1,149 @@ +/* +Copyright 2025 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 controller provides utils for controller-runtime. +package controller + +import ( + "context" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "sigs.k8s.io/cluster-api/feature" + "sigs.k8s.io/cluster-api/util/cache" +) + +type reconcilerWrapper struct { + name string + reconcileCache cache.Cache[reconcileCacheEntry] + reconciler reconcile.Reconciler + rateLimitInterval time.Duration + queueRateLimiter *typedItemExponentialFailureRateLimiter[reconcile.Request] +} + +func (r *reconcilerWrapper) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { + if !feature.Gates.Enabled(feature.ReconcilerRateLimiting) { + return r.reconciler.Reconcile(ctx, req) + } + + reconcileStartTime := time.Now() + + // Check reconcileCache to ensure we won't run reconcile too frequently. + if cacheEntry, ok := r.reconcileCache.Has(reconcileCacheEntry{Request: req}.Key()); ok { + if requeueAfter, requeue := cacheEntry.ShouldRequeue(reconcileStartTime); requeue { + return ctrl.Result{RequeueAfter: requeueAfter}, nil + } + } + + // Add entry to the reconcileCache so we won't run Reconcile more than once per second. + // Under certain circumstances the ReconcileAfter time will be set to a later time via DeferNextReconcile / + // DeferNextReconcileForObject, e.g. when we're waiting for Pods to terminate during node drain or + // volumes to detach. This is done to ensure we're not spamming the workload cluster API server. + r.reconcileCache.Add(reconcileCacheEntry{Request: req, ReconcileAfter: reconcileStartTime.Add(r.rateLimitInterval)}) + + // Update metrics after processing each item + defer func() { + reconcileTime.WithLabelValues(r.name).Observe(time.Since(reconcileStartTime).Seconds()) + }() + + result, err := r.reconciler.Reconcile(ctx, req) + if err != nil { + // Note: controller-runtime logs a warning if an error is returned in combination with + // RequeueAfter / Requeue. Dropping RequeueAfter and Requeue here to avoid this warning + // (while preserving Priority). + result.RequeueAfter = 0 + result.Requeue = false //nolint:staticcheck // We have to handle Requeue until it is removed + } + switch { + case err != nil: + reconcileTotal.WithLabelValues(r.name, labelError).Inc() + case result.RequeueAfter > 0: + // It does not make sense to use a requeueAfter that is lower than the rate-limiting enforced via + // the reconcileCache, so we use that as a minimum. + // Note: Evaluating this here includes the reconcileCache entry set above, but also the entry that + // might have been added during Reconcile via DeferNextReconcile / DeferNextReconcileForObject. + // TODO: It would also be possible to extend r.queueRateLimiter to set a request-specific minimum requeueAfter. + // This would allow us to also enforce a minimum requeueAfter for the err != nil and Requeue cases. + minimumRequeueAfter := r.rateLimitInterval + if cacheEntry, ok := r.reconcileCache.Has(reconcileCacheEntry{Request: req}.Key()); ok { + if requeueAfter, requeue := cacheEntry.ShouldRequeue(time.Now()); requeue { + minimumRequeueAfter = requeueAfter + } + } + result.RequeueAfter = max(result.RequeueAfter, minimumRequeueAfter) + r.queueRateLimiter.ActualForget(req) + reconcileTotal.WithLabelValues(r.name, labelRequeueAfter).Inc() + case result.Requeue: //nolint: staticcheck // We have to handle Requeue until it is removed + reconcileTotal.WithLabelValues(r.name, labelRequeue).Inc() + default: + r.queueRateLimiter.ActualForget(req) + reconcileTotal.WithLabelValues(r.name, labelSuccess).Inc() + } + + return result, err +} + +type controllerWrapper struct { + controller.TypedController[reconcile.Request] + reconcileCache cache.Cache[reconcileCacheEntry] +} + +func (c *controllerWrapper) DeferNextReconcile(req reconcile.Request, reconcileAfter time.Time) { + c.reconcileCache.Add(reconcileCacheEntry{ + Request: req, + ReconcileAfter: reconcileAfter, + }) +} + +func (c *controllerWrapper) DeferNextReconcileForObject(obj metav1.Object, reconcileAfter time.Time) { + c.DeferNextReconcile(reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: obj.GetNamespace(), + Name: obj.GetName(), + }}, reconcileAfter) +} + +// reconcileCacheEntry is an Entry for the Cache that stores the +// earliest time after which the next Reconcile should be executed. +type reconcileCacheEntry struct { + Request reconcile.Request + ReconcileAfter time.Time +} + +var _ cache.Entry = &reconcileCacheEntry{} + +// Key returns the cache key of a reconcileCacheEntry. +func (r reconcileCacheEntry) Key() string { + return r.Request.String() +} + +// ShouldRequeue returns if the current Reconcile should be requeued. +func (r reconcileCacheEntry) ShouldRequeue(now time.Time) (requeueAfter time.Duration, requeue bool) { + if r.ReconcileAfter.IsZero() { + return time.Duration(0), false + } + + if r.ReconcileAfter.After(now) { + return r.ReconcileAfter.Sub(now), true + } + + return time.Duration(0), false +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/controller_fake.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/controller_fake.go new file mode 100644 index 0000000000..e2cd9780fd --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/controller_fake.go @@ -0,0 +1,47 @@ +/* +Copyright 2025 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 controller + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +func NewFakeController() *FakeController { + return &FakeController{ + Deferrals: map[reconcile.Request]time.Time{}, + } +} + +type FakeController struct { + controller.Controller + Deferrals map[reconcile.Request]time.Time +} + +func (f *FakeController) DeferNextReconcile(req reconcile.Request, reconcileAfter time.Time) { + f.Deferrals[req] = reconcileAfter +} + +func (f *FakeController) DeferNextReconcileForObject(obj metav1.Object, reconcileAfter time.Time) { + f.Deferrals[reconcile.Request{ + NamespacedName: types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}, + }] = reconcileAfter +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/metrics.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/metrics.go new file mode 100644 index 0000000000..348b69f4a8 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/controller/metrics.go @@ -0,0 +1,67 @@ +/* +Copyright 2025 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 controller + +import ( + "time" + + "github.com/prometheus/client_golang/prometheus" + "sigs.k8s.io/controller-runtime/pkg/metrics" +) + +// Note: Additional metrics have been added because the ReconcileTotal & ReconcileTime +// controller-runtime metrics are also counting the Requeues for rate-limiting. +// Note: The following controller-runtime metrics are correct even with rate-limiting: +// ReconcileErrors, TerminalReconcileErrors, ReconcilePanics, WorkerCount, ActiveWorkers. + +var ( + // reconcileTotal is a prometheus counter metrics which holds the total + // number of reconciliations per controller. It has two labels. controller label refers + // to the controller name and result label refers to the reconcile result i.e. + // success, error, requeue, requeue_after. + // Note: The difference between the CAPI and the controller-runtime metric is that the + // CAPI metric excludes rate-limiting related reconciles. + reconcileTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "capi_reconcile_total", + Help: "Total number of reconciliations per controller", + }, []string{"controller", "result"}) + + // reconcileTime is a prometheus metric which keeps track of the duration + // of reconciliations. + // Note: The difference between the CAPI and the controller-runtime metric is that the + // CAPI metric excludes rate-limiting related reconciles. + reconcileTime = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "capi_reconcile_time_seconds", + Help: "Length of time per reconciliation per controller", + Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, + 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 40, 50, 60}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, + }, []string{"controller"}) +) + +const ( + labelError = "error" + labelRequeueAfter = "requeue_after" + labelRequeue = "requeue" + labelSuccess = "success" +) + +func init() { + metrics.Registry.MustRegister(reconcileTotal, reconcileTime) +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/inplace/inplace.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/inplace/inplace.go new file mode 100644 index 0000000000..6de704efc3 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/inplace/inplace.go @@ -0,0 +1,69 @@ +/* +Copyright 2025 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 inplace provides utils for in-place updates. +package inplace + +import ( + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" + "sigs.k8s.io/cluster-api/internal/hooks" +) + +// IsUpdateInProgress returns true if an in-place update is in progress for one machine. +// Note: an in-place update is considered in progress even if technically it is still starting +// +// (only the annotation is there, the hook is not yet there), or if it is stopping (only the +// pending hook is still there, but the annotation is gone). +func IsUpdateInProgress(machine *clusterv1.Machine) bool { + _, inPlaceUpdateInProgress := machine.Annotations[clusterv1.UpdateInProgressAnnotation] + hasUpdateMachinePending := hooks.IsPending(runtimehooksv1.UpdateMachine, machine) + + return inPlaceUpdateInProgress || hasUpdateMachinePending +} + +// CleanupMachineSpecForDiff cleans up a MachineSpec for diff. +// Note: Please update mdutil.MachineTemplateDeepCopyRolloutFields accordingly if necessary. +func CleanupMachineSpecForDiff(spec *clusterv1.MachineSpec) *clusterv1.MachineSpec { + spec = spec.DeepCopy() + + // The following fields are set to their zero value so they are omitted from the comparison, + // because they should never be the reason for an in-place update. + + // Should never change. + spec.ClusterName = "" + + // Machine: should never change. + // MachineSet: not responsibility of the in-place update extension. + spec.Bootstrap = clusterv1.Bootstrap{} + spec.InfrastructureRef = clusterv1.ContractVersionedObjectReference{} + + // Machine: should never change. + // MachineSet: should not be set. + spec.ProviderID = "" + + // Version & FailureDomain should be compared. + + // Fields that are mutated in-place without a rollout. + spec.MinReadySeconds = nil + spec.ReadinessGates = nil + spec.Deletion.NodeDrainTimeoutSeconds = nil + spec.Deletion.NodeVolumeDetachTimeoutSeconds = nil + spec.Deletion.NodeDeletionTimeoutSeconds = nil + spec.Taints = nil + + return spec +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/patch/patch.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/patch/patch.go new file mode 100644 index 0000000000..795ce39850 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/patch/patch.go @@ -0,0 +1,249 @@ +/* +Copyright 2025 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 patch contains patch utils. +package patch + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "runtime/debug" + "strings" + + jsonpatch "github.com/evanphx/json-patch/v5" + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" + kerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + + runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" + "sigs.k8s.io/cluster-api/internal/contract" +) + +// ApplyPatchToTypedObject applies the patch to a typed obj. +func ApplyPatchToTypedObject[T any](ctx context.Context, currentMachine *T, machinePath runtimehooksv1.Patch, patchPath string) error { + // Note: Machine needs special handling because it is not a runtime.RawExtension. Simply converting it here to + // a runtime.RawExtension so we can avoid making the code in applyPatchToObject more complex. + currentMachineRaw, err := ConvertToRawExtension(currentMachine) + if err != nil { + return err + } + + machineChanged, err := ApplyPatchToObject(ctx, ¤tMachineRaw, machinePath, patchPath) + if err != nil { + return err + } + + if !machineChanged { + return nil + } + + // Note: json.Unmarshal can't be used directly on *currentMachine as json.Unmarshal does not unset fields. + patchedCurrentMachine := new(T) + if err := json.Unmarshal(currentMachineRaw.Raw, patchedCurrentMachine); err != nil { + return err + } + *currentMachine = *patchedCurrentMachine + return nil +} + +// ApplyPatchToObject applies the patch to the obj. +// Note: This is following the same general structure that is used in the applyPatchToRequest func in +// internal/controllers/topology/cluster/patches/engine.go. +func ApplyPatchToObject(ctx context.Context, obj *runtime.RawExtension, patch runtimehooksv1.Patch, patchPath string) (objChanged bool, reterr error) { + log := ctrl.LoggerFrom(ctx) + + if patch.PatchType == "" { + return false, errors.Errorf("failed to apply patch: patchType is not set") + } + + defer func() { + if r := recover(); r != nil { + log.Info(fmt.Sprintf("Observed a panic when applying patch: %v\n%s", r, string(debug.Stack()))) + reterr = kerrors.NewAggregate([]error{reterr, fmt.Errorf("observed a panic when applying patch: %v", r)}) + } + }() + + // Create a copy of obj.Raw. + // The patches will be applied to the copy and then only spec changes will be copied back to the obj. + patchedObject := bytes.Clone(obj.Raw) + var err error + + switch patch.PatchType { + case runtimehooksv1.JSONPatchType: + log.V(5).Info("Accumulating JSON patch", "patch", string(patch.Patch)) + jsonPatch, err := jsonpatch.DecodePatch(patch.Patch) + if err != nil { + log.Error(err, "Failed to apply patch: error decoding json patch (RFC6902)", "patch", string(patch.Patch)) + return false, errors.Wrap(err, "failed to apply patch: error decoding json patch (RFC6902)") + } + + if len(jsonPatch) == 0 { + // Return if there are no patches, nothing to do. + return false, nil + } + + patchedObject, err = jsonPatch.Apply(patchedObject) + if err != nil { + log.Error(err, "Failed to apply patch: error applying json patch (RFC6902)", "patch", string(patch.Patch)) + return false, errors.Wrap(err, "failed to apply patch: error applying json patch (RFC6902)") + } + case runtimehooksv1.JSONMergePatchType: + if len(patch.Patch) == 0 || bytes.Equal(patch.Patch, []byte("{}")) { + // Return if there are no patches, nothing to do. + return false, nil + } + + log.V(5).Info("Accumulating JSON merge patch", "patch", string(patch.Patch)) + patchedObject, err = jsonpatch.MergePatch(patchedObject, patch.Patch) + if err != nil { + log.Error(err, "Failed to apply patch: error applying json merge patch (RFC7386)", "patch", string(patch.Patch)) + return false, errors.Wrap(err, "failed to apply patch: error applying json merge patch (RFC7386)") + } + default: + return false, errors.Errorf("failed to apply patch: unknown patchType %s", patch.PatchType) + } + + // Overwrite the spec of obj with the spec of the patchedObject, + // to ensure that we only pick up changes to the spec. + if err := Patch(obj, patchedObject, patchPath); err != nil { + return false, errors.Wrap(err, "failed to apply patch to object") + } + + return true, nil +} + +// ConvertToRawExtension converts any object to a runtime.RawExtension. +func ConvertToRawExtension(object any) (runtime.RawExtension, error) { + objectBytes, err := json.Marshal(object) + if err != nil { + return runtime.RawExtension{}, errors.Wrap(err, "failed to marshal object to JSON") + } + + objectUnstructured, ok := object.(*unstructured.Unstructured) + if !ok { + objectUnstructured = &unstructured.Unstructured{} + // Note: This only succeeds if object has apiVersion & kind set (which is always the case). + if err := json.Unmarshal(objectBytes, objectUnstructured); err != nil { + return runtime.RawExtension{}, errors.Wrap(err, "failed to Unmarshal object into Unstructured") + } + } + + // Note: Raw and Object are always both set and Object is always set as an Unstructured + // to simplify subsequent code in matchesUnstructuredSpec. + return runtime.RawExtension{ + Raw: objectBytes, + Object: objectUnstructured, + }, nil +} + +// Patch overwrites spec in object with spec of patchedObjectBytes. +func Patch(object *runtime.RawExtension, patchedObjectBytes []byte, patchPath string) error { + objectUnstructured, err := bytesToUnstructured(object.Raw) + if err != nil { + return errors.Wrap(err, "failed to convert object to Unstructured") + } + patchedObjectUnstructured, err := bytesToUnstructured(patchedObjectBytes) + if err != nil { + return errors.Wrap(err, "failed to convert patched object to Unstructured") + } + + // Copy spec from patchedObjectUnstructured to objectUnstructured. + if err := CopySpec(CopySpecInput{ + Src: patchedObjectUnstructured, + Dest: objectUnstructured, + SrcSpecPath: patchPath, + DestSpecPath: patchPath, + }); err != nil { + return errors.Wrap(err, "failed to apply patch to object") + } + + // Marshal objectUnstructured and store it in object. + objectBytes, err := objectUnstructured.MarshalJSON() + if err != nil { + return errors.Wrapf(err, "failed to marshal patched object") + } + object.Object = objectUnstructured + object.Raw = objectBytes + return nil +} + +// CopySpecInput is a struct containing the input parameters of CopySpec. +type CopySpecInput struct { + Src *unstructured.Unstructured + Dest *unstructured.Unstructured + SrcSpecPath string + DestSpecPath string + FieldsToPreserve []contract.Path +} + +// CopySpec copies a field from a srcSpecPath in src to a destSpecPath in dest, +// while preserving fieldsToPreserve. +func CopySpec(in CopySpecInput) error { + // Backup fields that should be preserved from dest. + preservedFields := map[string]interface{}{} + for _, field := range in.FieldsToPreserve { + value, found, err := unstructured.NestedFieldNoCopy(in.Dest.Object, field...) + if !found { + // Continue if the field does not exist in src. fieldsToPreserve don't have to exist. + continue + } else if err != nil { + return errors.Wrapf(err, "failed to get field %q from %s %s", strings.Join(field, "."), in.Dest.GetKind(), klog.KObj(in.Dest)) + } + preservedFields[strings.Join(field, ".")] = value + } + + // Get spec from src. + srcSpec, found, err := unstructured.NestedFieldNoCopy(in.Src.Object, strings.Split(in.SrcSpecPath, ".")...) + if !found { + // Return if srcSpecPath does not exist in src, nothing to do. + return nil + } else if err != nil { + return errors.Wrapf(err, "failed to get field %q from %s %s", in.SrcSpecPath, in.Src.GetKind(), klog.KObj(in.Src)) + } + + // Set spec in dest. + if err := unstructured.SetNestedField(in.Dest.Object, srcSpec, strings.Split(in.DestSpecPath, ".")...); err != nil { + return errors.Wrapf(err, "failed to set field %q on %s %s", in.DestSpecPath, in.Dest.GetKind(), klog.KObj(in.Dest)) + } + + // Restore preserved fields. + for path, value := range preservedFields { + if err := unstructured.SetNestedField(in.Dest.Object, value, strings.Split(path, ".")...); err != nil { + return errors.Wrapf(err, "failed to set field %q on %s %s", path, in.Dest.GetKind(), klog.KObj(in.Dest)) + } + } + return nil +} + +// unstructuredDecoder is used to decode byte arrays into Unstructured objects. +var unstructuredDecoder = serializer.NewCodecFactory(nil).UniversalDeserializer() + +// bytesToUnstructured provides a utility method that converts a (JSON) byte array into an Unstructured object. +func bytesToUnstructured(b []byte) (*unstructured.Unstructured, error) { + // Unmarshal the JSON. + u := &unstructured.Unstructured{} + if _, _, err := unstructuredDecoder.Decode(b, nil, u); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal object from json") + } + + return u, nil +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/cache.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/cache.go index 0587e89335..e8bf7e8997 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/cache.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/cache.go @@ -104,18 +104,18 @@ func (r *ssaCache) Has(key, kind string) bool { // ComputeRequestIdentifier computes a request identifier for the cache. // The identifier is unique for a specific request to ensure we don't have to re-run the request // once we found out that it would not produce a diff. -// The identifier consists of: gvk, namespace, name and resourceVersion of the original object and a hash of the modified -// object. This ensures that we re-run the request as soon as either original or modified changes. -func ComputeRequestIdentifier(scheme *runtime.Scheme, original, modified client.Object) (string, error) { - modifiedObjectHash, err := hash.Compute(modified) +// The identifier consists of: gvk, namespace, name and resourceVersion of the object and a hash of the modified +// object. This ensures that we re-run the request as soon as anything changes. +func ComputeRequestIdentifier(scheme *runtime.Scheme, resourceVersion string, obj client.Object) (string, error) { + objHash, err := hash.Compute(obj) if err != nil { - return "", errors.Wrapf(err, "failed to calculate request identifier: failed to compute hash for modified object") + return "", errors.Wrapf(err, "failed to calculate request identifier: failed to compute hash for object") } - gvk, err := apiutil.GVKForObject(original, scheme) + gvk, err := apiutil.GVKForObject(obj, scheme) if err != nil { - return "", errors.Wrapf(err, "failed to calculate request identifier: failed to get GroupVersionKind of original object %s", klog.KObj(original)) + return "", errors.Wrapf(err, "failed to calculate request identifier: failed to get GroupVersionKind of object %s", klog.KObj(obj)) } - return fmt.Sprintf("%s.%s.%s.%d", gvk.String(), klog.KObj(original), original.GetResourceVersion(), modifiedObjectHash), nil + return fmt.Sprintf("%s.%s.%s.%d", gvk.String(), klog.KObj(obj), resourceVersion, objHash), nil } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/filterintent.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/filterintent.go index c3c776407d..0429ab795a 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/filterintent.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/filterintent.go @@ -17,10 +17,6 @@ limitations under the License. package ssa import ( - "fmt" - "reflect" - "strings" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/cluster-api/internal/contract" @@ -36,43 +32,26 @@ type FilterObjectInput struct { // spec.ControlPlaneEndpoint. // NOTE: ignore paths which point to an array are not supported by the current implementation. IgnorePaths []contract.Path - - // DropEmptyStructAndNil instructs the Helper to drop all fields with values equal to empty struct or nil. - // NOTE: This is required when using typed objects, because the DefaultUnstructuredConverter does - // not handle omitzero (yet). - DropEmptyStructAndNil bool } // FilterObject filter out changes not relevant for the controller. func FilterObject(obj *unstructured.Unstructured, input *FilterObjectInput) { // filter out changes not in the allowed paths (fields to not consider, e.g. status); - // also drop empty struct if required. if len(input.AllowedPaths) > 0 { FilterIntent(&FilterIntentInput{ - Path: contract.Path{}, - Value: obj.Object, - ShouldFilter: IsPathNotAllowed(input.AllowedPaths), - DropEmptyStructAndNil: input.DropEmptyStructAndNil, + Path: contract.Path{}, + Value: obj.Object, + ShouldFilter: IsPathNotAllowed(input.AllowedPaths), }) } // filter out changes for ignore paths (well known fields owned by other controllers, e.g. - // spec.controlPlaneEndpoint in the InfrastructureCluster object); also drop empty struct if required. + // spec.controlPlaneEndpoint in the InfrastructureCluster object); if len(input.IgnorePaths) > 0 { FilterIntent(&FilterIntentInput{ - Path: contract.Path{}, - Value: obj.Object, - ShouldFilter: IsPathIgnored(input.IgnorePaths), - DropEmptyStructAndNil: input.DropEmptyStructAndNil, - }) - } - - // DropEmptyStructAndNil if not already done above. - if input.DropEmptyStructAndNil && len(input.AllowedPaths) == 0 && len(input.IgnorePaths) == 0 { - FilterIntent(&FilterIntentInput{ - Path: contract.Path{}, - Value: obj.Object, - DropEmptyStructAndNil: input.DropEmptyStructAndNil, + Path: contract.Path{}, + Value: obj.Object, + ShouldFilter: IsPathIgnored(input.IgnorePaths), }) } } @@ -84,71 +63,38 @@ func FilterObject(obj *unstructured.Unstructured, input *FilterObjectInput) { // all of them are defined in reconcile_state.go and are targeting well-known fields inside nested maps. // Allowed paths / ignore paths which point to an array are not supported by the current implementation. func FilterIntent(ctx *FilterIntentInput) bool { + value, ok := ctx.Value.(map[string]interface{}) + if !ok { + return false + } + gotDeletions := false + for field := range value { + fieldCtx := &FilterIntentInput{ + // Compose the Path for the nested field. + Path: ctx.Path.Append(field), + // Gets the original and the modified Value for the field. + Value: value[field], + // Carry over global values from the context. + ShouldFilter: ctx.ShouldFilter, + } - switch value := ctx.Value.(type) { - case map[string]interface{}: - for field := range value { - fieldCtx := &FilterIntentInput{ - // Compose the Path for the nested field. - Path: ctx.Path.Append(field), - // Gets the original and the modified Value for the field. - Value: value[field], - // Carry over global values from the context. - ShouldFilter: ctx.ShouldFilter, - DropEmptyStructAndNil: ctx.DropEmptyStructAndNil, - } + // If the field should be filtered out, delete it from the modified object. + if fieldCtx.ShouldFilter != nil && fieldCtx.ShouldFilter(fieldCtx.Path) { + delete(value, field) + gotDeletions = true + continue + } - // If the field should be filtered out, delete it from the modified object. - if fieldCtx.ShouldFilter != nil && fieldCtx.ShouldFilter(fieldCtx.Path) { + // Process nested fields and get in return if FilterIntent removed fields. + if FilterIntent(fieldCtx) { + gotDeletions = true + // Ensure we are not leaving empty maps around. + if v, ok := fieldCtx.Value.(map[string]interface{}); ok && len(v) == 0 { delete(value, field) - gotDeletions = true - continue - } - - // TODO: Can be removed once we bumped to k8s.io v0.34 because the DefaultUnstructuredConverter will then handle omitzero - if strings.HasPrefix(fieldCtx.Path.String(), "spec") && fieldCtx.DropEmptyStructAndNil { - // If empty struct should be dropped and the value is a empty struct, delete it from the modified object. - if reflect.DeepEqual(fieldCtx.Value, map[string]interface{}{}) { - delete(value, field) - gotDeletions = true - continue - } - // If nil should be dropped and the value is nil, delete it from the modified object. - if reflect.DeepEqual(fieldCtx.Value, nil) { - delete(value, field) - gotDeletions = true - continue - } - } - - // Process nested fields and get in return if FilterIntent removed fields. - if FilterIntent(fieldCtx) { - gotDeletions = true - // Ensure we are not leaving empty maps around. - if v, ok := fieldCtx.Value.(map[string]interface{}); ok && len(v) == 0 { - delete(value, field) - } - } - } - case []interface{}: - // TODO: Can be removed once we bumped to k8s.io v0.34 because the DefaultUnstructuredConverter will then handle omitzero - if strings.HasPrefix(ctx.Path.String(), "spec") && ctx.DropEmptyStructAndNil { - for i, v := range value { - fieldCtx := &FilterIntentInput{ - // Compose the Path for the nested field. - Path: ctx.Path.Append(fmt.Sprintf("[%d]", i)), - // Not supporting ShouldFilter within arrays, so not setting it. - Value: v, - DropEmptyStructAndNil: ctx.DropEmptyStructAndNil, - } - if FilterIntent(fieldCtx) { - gotDeletions = true - } } } } - return gotDeletions } @@ -163,8 +109,6 @@ type FilterIntentInput struct { // ShouldFilter handle the func that determine if the current Path should be dropped or not. ShouldFilter func(path contract.Path) bool - - DropEmptyStructAndNil bool } // IsPathAllowed returns true when the Path is one of the AllowedPaths. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/managedfields.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/managedfields.go index 7db03983a0..b41a04620f 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/managedfields.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/managedfields.go @@ -18,163 +18,213 @@ limitations under the License. package ssa import ( + "bytes" "context" "encoding/json" + "time" "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/util/retry" "k8s.io/klog/v2" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/internal/contract" ) const classicManager = "manager" -// DropManagedFields modifies the managedFields entries on the object that belong to "manager" (Operation=Update) -// to drop ownership of the given paths if there is no field yet that is managed by `ssaManager`. -// -// If we want to be able to drop fields that were previously owned by the "manager" we have to ensure that -// fields are not co-owned by "manager" and `ssaManager`. Otherwise, when we drop the fields with SSA -// (i.e. `ssaManager`) the fields would remain as they are still owned by "manager". -// The following code will do a one-time update on the managed fields. -// We won't do this on subsequent reconciles. This case will be identified by checking if `ssaManager` owns any fields. -// Dropping ownership in paths for existing "manager" entries (which could also be from other controllers) is safe, -// as we assume that if other controllers are still writing fields on the object they will just do it again and thus -// gain ownership again. -func DropManagedFields(ctx context.Context, c client.Client, obj client.Object, ssaManager string, paths []contract.Path) error { - // Return if `ssaManager` already owns any fields. - if hasFieldsManagedBy(obj, ssaManager) { - return nil +// RemoveManagedFieldsForLabelsAndAnnotations removes labels and annotations from managedFields entries. +// This func is e.g. used in the following context: +// * First a full object is created with fieldManager. +// * Then this func is called to drop ownership for labels and annotations +// * And then in a subsequent syncMachines call a metadataFieldManager can take ownership for labels and annotations. +// Note: This is done so that this func does not rely on managedFields being stored in the cache, so we can optimize +// memory usage by dropping managedFields before storing objects in the cache. +func RemoveManagedFieldsForLabelsAndAnnotations(ctx context.Context, c client.Client, apiReader client.Reader, object client.Object, fieldManager string) error { + objectKey := client.ObjectKeyFromObject(object) + objectGVK, err := apiutil.GVKForObject(object, c.Scheme()) + if err != nil { + return errors.Wrapf(err, "failed to remove managedFields for labels and annotations from object %s", + klog.KRef(objectKey.Namespace, objectKey.Name)) } - // Since there is no field managed by `ssaManager` it means that - // this is the first time this object is being processed after the controller calling this function - // started to use SSA patches. - // It is required to clean-up managedFields from entries created by the regular patches. - // This will ensure that `ssaManager` will be able to modify the fields that - // were originally owned by "manager". - base := obj.DeepCopyObject().(client.Object) - - // Modify managedFieldEntry for manager=manager and operation=update to drop ownership - // for the given paths to avoid having two managers holding values. - originalManagedFields := obj.GetManagedFields() - managedFields := make([]metav1.ManagedFieldsEntry, 0, len(originalManagedFields)) - for _, managedField := range originalManagedFields { - if managedField.Manager == classicManager && - managedField.Operation == metav1.ManagedFieldsOperationUpdate { - // Unmarshal the managed fields into a map[string]interface{} - fieldsV1 := map[string]interface{}{} - if err := json.Unmarshal(managedField.FieldsV1.Raw, &fieldsV1); err != nil { - return errors.Wrap(err, "failed to unmarshal managed fields") - } - - // Filter out the ownership for the given paths. - FilterIntent(&FilterIntentInput{ - Path: contract.Path{}, - Value: fieldsV1, - ShouldFilter: IsPathIgnored(paths), - }) - - fieldsV1Raw, err := json.Marshal(fieldsV1) - if err != nil { - return errors.Wrap(err, "failed to marshal managed fields") + var getObjectFromAPIServer bool + err = retry.RetryOnConflict(wait.Backoff{ + Steps: 3, + Duration: 10 * time.Millisecond, + Factor: 1.0, + Jitter: 0.1, + }, func() error { + // First try is with the passed in object. After that get the object from the apiserver. + // Note: This is done so that this func does not rely on managedFields being stored in the cache, so we can optimize + // memory usage by dropping managedFields before storing objects in the cache. + if getObjectFromAPIServer { + if err := apiReader.Get(ctx, objectKey, object); err != nil { + return err } - managedField.FieldsV1.Raw = fieldsV1Raw - - managedFields = append(managedFields, managedField) } else { - // Do not modify the entry. Use as is. - managedFields = append(managedFields, managedField) + getObjectFromAPIServer = true } - } - obj.SetManagedFields(managedFields) + base := object.DeepCopyObject().(client.Object) + + // Modify managedFields for manager=fieldManager and operation=Apply to drop ownership for labels and annotations. + originalManagedFields := object.GetManagedFields() + managedFields := make([]metav1.ManagedFieldsEntry, 0, len(originalManagedFields)) + for _, managedField := range originalManagedFields { + if managedField.Manager == fieldManager && + managedField.Operation == metav1.ManagedFieldsOperationApply && + managedField.Subresource == "" { + // If fieldManager does not own labels and annotations there's nothing to do. + if !bytes.Contains(managedField.FieldsV1.Raw, []byte("f:metadata")) { + return nil + } + + // Unmarshal the managed fields into a map[string]interface{} + fieldsV1 := map[string]interface{}{} + if err := json.Unmarshal(managedField.FieldsV1.Raw, &fieldsV1); err != nil { + return errors.Wrap(err, "failed to unmarshal managed fields") + } + + // Filter out the ownership for labels and annotations. + deletedFields := FilterIntent(&FilterIntentInput{ + Path: contract.Path{}, + Value: fieldsV1, + ShouldFilter: IsPathIgnored([]contract.Path{ + {"f:metadata", "f:annotations"}, + {"f:metadata", "f:labels"}, + }), + }) + if !deletedFields { + // If fieldManager did not own labels and annotations there's nothing to do. + return nil + } + + fieldsV1Raw, err := json.Marshal(fieldsV1) + if err != nil { + return errors.Wrap(err, "failed to marshal managed fields") + } + managedField.FieldsV1.Raw = fieldsV1Raw + + managedFields = append(managedFields, managedField) + } else { + // Do not modify the entry. Use as is. + managedFields = append(managedFields, managedField) + } + } + object.SetManagedFields(managedFields) - return c.Patch(ctx, obj, client.MergeFrom(base)) + // Use optimistic locking to avoid accidentally rolling back managedFields. + return c.Patch(ctx, object, client.MergeFromWithOptions(base, client.MergeFromWithOptimisticLock{})) + }) + if err != nil { + return errors.Wrapf(err, "failed to remove managedFields for labels and annotations from %s %s", + objectGVK.Kind, klog.KRef(objectKey.Namespace, objectKey.Name)) + } + return nil } -// CleanUpManagedFieldsForSSAAdoption deletes the managedFields entries on the object that belong to "manager" (Operation=Update) -// if there is no field yet that is managed by `ssaManager`. -// It adds an "empty" entry in managedFields of the object if no field is currently managed by `ssaManager`. -// -// In previous versions of Cluster API (< v1.4.0) we were writing objects with Create and Patch which resulted in fields -// being owned by the "manager". After switching to Server-Side-Apply (SSA), fields will be owned by `ssaManager`. -// -// If we want to be able to drop fields that were previously owned by the "manager" we have to ensure that -// fields are not co-owned by "manager" and `ssaManager`. Otherwise, when we drop the fields with SSA -// (i.e. `ssaManager`) the fields would remain as they are still owned by "manager". -// The following code will do a one-time update on the managed fields to drop all entries for "manager". -// We won't do this on subsequent reconciles. This case will be identified by checking if `ssaManager` owns any fields. -// Dropping all existing "manager" entries (which could also be from other controllers) is safe, as we assume that if -// other controllers are still writing fields on the object they will just do it again and thus gain ownership again. -func CleanUpManagedFieldsForSSAAdoption(ctx context.Context, c client.Client, obj client.Object, ssaManager string) error { - // Return if `ssaManager` already owns any fields. - if hasFieldsManagedBy(obj, ssaManager) { +// MigrateManagedFields migrates managedFields. +// ManagedFields are only migrated if fieldManager owns labels+annotations +// The migration deletes all non-status managedField entries for fieldManager:Apply and manager:Update. +// Additionally it adds a seed entry for metadataFieldManager. +// Note: We have to call this func for every Machine created with CAPI <= v1.11 once. +// Given that this was introduced in CAPI v1.12 and our n-3 upgrade policy this can +// be removed with CAPI v1.15. +func MigrateManagedFields(ctx context.Context, c client.Client, object client.Object, fieldManager, metadataFieldManager string) error { + objectKey := client.ObjectKeyFromObject(object) + objectGVK, err := apiutil.GVKForObject(object, c.Scheme()) + if err != nil { + return errors.Wrapf(err, "failed to migrate managedFields for object %s", + klog.KRef(objectKey.Namespace, objectKey.Name)) + } + + // Check if a migration is still needed. This should be only done once per object. + needsMigration, err := needsMigration(object, fieldManager) + if err != nil { + return errors.Wrapf(err, "failed to migrate managedFields for %s %s", + objectGVK.Kind, klog.KRef(objectKey.Namespace, objectKey.Name)) + } + if !needsMigration { return nil } - // Since there is no field managed by `ssaManager` it means that - // this is the first time this object is being processed after the controller calling this function - // started to use SSA patches. - // It is required to clean-up managedFields from entries created by the regular patches. - // This will ensure that `ssaManager` will be able to modify the fields that - // were originally owned by "manager". - base := obj.DeepCopyObject().(client.Object) + base := object.DeepCopyObject().(client.Object) - // Remove managedFieldEntry for manager=manager and operation=update to prevent having two managers holding values. - originalManagedFields := obj.GetManagedFields() + // Remove managedFields for fieldManager:Apply and manager:Update. + originalManagedFields := object.GetManagedFields() managedFields := make([]metav1.ManagedFieldsEntry, 0, len(originalManagedFields)) - for i := range originalManagedFields { - if originalManagedFields[i].Manager == classicManager && - originalManagedFields[i].Operation == metav1.ManagedFieldsOperationUpdate { + for _, managedField := range originalManagedFields { + if managedField.Manager == fieldManager && + managedField.Operation == metav1.ManagedFieldsOperationApply && + managedField.Subresource == "" { + continue + } + if managedField.Manager == classicManager && + managedField.Operation == metav1.ManagedFieldsOperationUpdate && + managedField.Subresource == "" { continue } - managedFields = append(managedFields, originalManagedFields[i]) + managedFields = append(managedFields, managedField) } - // Add a seeding managedFieldEntry for SSA executed by the management controller, to prevent SSA to create/infer - // a default managedFieldEntry when the first SSA is applied. - // More specifically, if an existing object doesn't have managedFields when applying the first SSA the API server - // creates an entry with operation=Update (kind of guessing where the object comes from), but this entry ends up + // Add a seeding managedField entry to prevent SSA to create/infer a default managedField entry when the + // first SSA is applied. + // If an existing object doesn't have managedFields when applying the first SSA the API server + // creates an entry with operation=Update (guessing where the object comes from), but this entry ends up // acting as a co-ownership and we want to prevent this. - // NOTE: fieldV1Map cannot be empty, so we add metadata.name which will be cleaned up at the first SSA patch of the same fieldManager. - fieldV1Map := map[string]interface{}{ - "f:metadata": map[string]interface{}{ - "f:name": map[string]interface{}{}, - }, - } - fieldV1, err := json.Marshal(fieldV1Map) - if err != nil { - return errors.Wrap(err, "failed to create seeding fieldV1Map for cleaning up legacy managed fields") - } - now := metav1.Now() - gvk, err := apiutil.GVKForObject(obj, c.Scheme()) - if err != nil { - return errors.Wrapf(err, "failed to get GroupVersionKind of object %s", klog.KObj(obj)) - } + // NOTE: fieldV1Map cannot be empty, so we add metadata.name which will be cleaned up at the first + // SSA patch of the same manager (updateLabelsAndAnnotations directly after calling this func). managedFields = append(managedFields, metav1.ManagedFieldsEntry{ - Manager: ssaManager, + Manager: metadataFieldManager, Operation: metav1.ManagedFieldsOperationApply, - APIVersion: gvk.GroupVersion().String(), - Time: &now, + APIVersion: objectGVK.GroupVersion().String(), + Time: ptr.To(metav1.Now()), FieldsType: "FieldsV1", - FieldsV1: &metav1.FieldsV1{Raw: fieldV1}, + FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:metadata":{"f:name":{}}}`)}, }) - obj.SetManagedFields(managedFields) + object.SetManagedFields(managedFields) - return c.Patch(ctx, obj, client.MergeFrom(base)) + // Use optimistic locking to avoid accidentally rolling back managedFields. + if err := c.Patch(ctx, object, client.MergeFromWithOptions(base, client.MergeFromWithOptimisticLock{})); err != nil { + return errors.Wrapf(err, "failed to migrate managedFields for %s %s", + objectGVK.Kind, klog.KRef(objectKey.Namespace, objectKey.Name)) + } + return nil } -// hasFieldsManagedBy returns true if any of the fields in obj are managed by manager. -func hasFieldsManagedBy(obj client.Object, manager string) bool { - managedFields := obj.GetManagedFields() - for _, mf := range managedFields { - if mf.Manager == manager { - return true +// needsMigration returns true if fieldManager:Apply owns the clusterv1.ClusterNameLabel. +func needsMigration(object client.Object, fieldManager string) (bool, error) { + for _, managedField := range object.GetManagedFields() { + //nolint:gocritic // much easier to read this way, not going to use continue instead + if managedField.Manager == fieldManager && + managedField.Operation == metav1.ManagedFieldsOperationApply && + managedField.Subresource == "" { + // If fieldManager does not own the cluster-name label migration is not needed + if !bytes.Contains(managedField.FieldsV1.Raw, []byte("f:cluster.x-k8s.io/cluster-name")) { + return false, nil + } + + // Unmarshal the managed fields into a map[string]interface{} + fieldsV1 := map[string]interface{}{} + if err := json.Unmarshal(managedField.FieldsV1.Raw, &fieldsV1); err != nil { + return false, errors.Wrap(err, "failed to determine if migration is needed: failed to unmarshal managed fields") + } + + // Note: MigrateManagedFields is only called for BootstrapConfig/InfraMachine and both always have the cluster-name label set. + _, containsClusterNameLabel, err := unstructured.NestedFieldNoCopy(fieldsV1, "f:metadata", "f:labels", "f:cluster.x-k8s.io/cluster-name") + if err != nil { + return false, errors.Wrapf(err, "failed to determine if migration is needed: failed to determine if managed field contains the %s label", clusterv1.ClusterNameLabel) + } + return containsClusterNameLabel, nil } } - return false + return false, nil } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/managedfieldsmitigation.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/managedfieldsmitigation.go new file mode 100644 index 0000000000..c5a45df80a --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/managedfieldsmitigation.go @@ -0,0 +1,245 @@ +/* +Copyright 2026 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 ssa + +import ( + "context" + "encoding/json" + "slices" + + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" + "k8s.io/utils/ptr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/structured-merge-diff/v6/fieldpath" + + bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" + controlplanev1beta1 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1" + controlplanev1 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2" + "sigs.k8s.io/cluster-api/util" +) + +const beforeFirstApplyManager = "before-first-apply" + +var kcpScheme = runtime.NewScheme() + +func init() { + _ = controlplanev1.AddToScheme(kcpScheme) + _ = controlplanev1beta1.AddToScheme(kcpScheme) +} + +// MitigateManagedFieldsIssue mitigates the managedField apiserver issue, +// where apiserver drops managedFields of an object under certain circumstances: +// https://github.com/kubernetes/kubernetes/issues/136919 +// +// It mitigates the issue by removing the before-first-apply entry (if it exists) +// and by ensuring that there is always a (minimal) entry for fieldManager to avoid +// that the apiserver adds a new before-first-apply entry during the next SSA call. +// NOTE: The fieldManager passed in should be used by subsequent SSA calls to increase +// the chances that the managedField entry gets cleaned up soon. +// +// A subsequent regular SSA call by our controller will fully restore managedFields. +// +// This addresses the issue in most scenarios, but it also means that an SSA call +// directly after we added the minimal entry won't be able to unset fields in general. +// +// Because we know that this issue is especially problematic with KCP extraArgs +// we added special handling for KCP to add extraArgs managedField entries based on +// the current KCP object. This allows to unset extraArgs on the next SSA call even +// directly after we just mitigated the issue. +// +// The following cases have to be handled: +// Case 0: no-op +// - [fieldManager] => [fieldManager] +// +// Case 1: no managedFields +// - [] => [fieldManager] +// +// Case 2: before-first-apply entry +// - a: [before-first-apply] => [fieldManager] +// - b: [before-first-apply,other managers ...] => [fieldManager,other managers ...] +// - c: [before-first-apply,fieldManager,other managers ...] => [fieldManager,other managers ...] +// +// We should keep this logic until the issue has been fixed in apiserver and our minimal supported +// Kubernetes version has the fix. +func MitigateManagedFieldsIssue(ctx context.Context, c client.Client, obj client.Object, fieldManager string) (bool, error) { + if util.IsNil(obj) { + // Return if object is nil. + return false, nil + } + + if len(obj.GetManagedFields()) > 0 && !slices.ContainsFunc(obj.GetManagedFields(), isManager(beforeFirstApplyManager)) { + // Return if object has managedFields and no before-first-apply entry. + return false, nil + } + + log := ctrl.LoggerFrom(ctx) + objGVK, err := apiutil.GVKForObject(obj, c.Scheme()) + if err != nil { + return false, errors.Wrapf(err, "failed to mitigate managedFields issue") + } + + // Remove before-first-apply entry if it exists. + managedFields := slices.DeleteFunc(obj.GetManagedFields(), isManager(beforeFirstApplyManager)) + + // Add fieldManager entry if it does not exist. + if !slices.ContainsFunc(obj.GetManagedFields(), isManager(fieldManager)) { + fieldsV1, err := computeManagedFields(obj, objGVK) + if err != nil { + return false, errors.Wrapf(err, "failed to mitigate managedFields issue: failed to compute managedFields entry") + } + managedFields = append(managedFields, metav1.ManagedFieldsEntry{ + Manager: fieldManager, + Operation: metav1.ManagedFieldsOperationApply, + APIVersion: objGVK.GroupVersion().String(), + Time: ptr.To(metav1.Now()), + FieldsType: "FieldsV1", + FieldsV1: &metav1.FieldsV1{Raw: fieldsV1}, + }) + } + + // Create a patch to update only managedFields. + // Include resourceVersion to avoid race conditions. + jsonPatch := []map[string]interface{}{ + { + "op": "replace", + "path": "/metadata/managedFields", + "value": managedFields, + }, + { + "op": "replace", + "path": "/metadata/resourceVersion", + "value": obj.GetResourceVersion(), + }, + } + patch, err := json.Marshal(jsonPatch) + if err != nil { + return false, errors.Wrap(err, "failed to mitigate managedFields issue: failed to marshal patch for managedFields entry") + } + + log.Info("Fixing up managedFields to mitigate kube-apiserver managedFields issue", objGVK.Kind, klog.KObj(obj)) + if err := c.Patch(ctx, obj, client.RawPatch(types.JSONPatchType, patch)); err != nil { + return false, errors.Wrapf(err, "failed to mitigate managedFields issue: failed to patch %s %s", objGVK.Kind, klog.KObj(obj)) + } + + return true, nil +} + +func computeManagedFields(obj client.Object, objGVK schema.GroupVersionKind) ([]byte, error) { + managedFieldSet := fieldpath.NewSet() + + switch { + // Compute managedField entry with extraArgs for v1beta2 KubeadmControlPlane. + case objGVK.Kind == "KubeadmControlPlane" && objGVK.Version == controlplanev1.GroupVersion.Version: + kcp := &controlplanev1.KubeadmControlPlane{} + if err := kcpScheme.Convert(obj, kcp, nil); err != nil { + return nil, errors.Wrap(err, "failed to convert object to KubeadmControlPlane") + } + + addArgs(managedFieldSet, kcp.Spec.KubeadmConfigSpec.ClusterConfiguration.APIServer.ExtraArgs, + "spec", "kubeadmConfigSpec", "clusterConfiguration", "apiServer", "extraArgs") + addArgs(managedFieldSet, kcp.Spec.KubeadmConfigSpec.ClusterConfiguration.ControllerManager.ExtraArgs, + "spec", "kubeadmConfigSpec", "clusterConfiguration", "controllerManager", "extraArgs") + addArgs(managedFieldSet, kcp.Spec.KubeadmConfigSpec.ClusterConfiguration.Scheduler.ExtraArgs, + "spec", "kubeadmConfigSpec", "clusterConfiguration", "scheduler", "extraArgs") + addArgs(managedFieldSet, kcp.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local.ExtraArgs, + "spec", "kubeadmConfigSpec", "clusterConfiguration", "etcd", "local", "extraArgs") + addArgs(managedFieldSet, kcp.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.KubeletExtraArgs, + "spec", "kubeadmConfigSpec", "initConfiguration", "nodeRegistration", "kubeletExtraArgs") + addArgs(managedFieldSet, kcp.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.KubeletExtraArgs, + "spec", "kubeadmConfigSpec", "joinConfiguration", "nodeRegistration", "kubeletExtraArgs") + // Compute managedField entry with extraArgs for v1beta1 KubeadmControlPlane. + case objGVK.Kind == "KubeadmControlPlane" && objGVK.Version == controlplanev1beta1.GroupVersion.Version: + kcp := &controlplanev1beta1.KubeadmControlPlane{} + if err := kcpScheme.Convert(obj, kcp, nil); err != nil { + return nil, errors.Wrap(err, "failed to convert object to KubeadmControlPlane") + } + + if kcp.Spec.KubeadmConfigSpec.ClusterConfiguration != nil { + addArgsV1Beta1(managedFieldSet, kcp.Spec.KubeadmConfigSpec.ClusterConfiguration.APIServer.ExtraArgs, + "spec", "kubeadmConfigSpec", "clusterConfiguration", "apiServer", "extraArgs") + addArgsV1Beta1(managedFieldSet, kcp.Spec.KubeadmConfigSpec.ClusterConfiguration.ControllerManager.ExtraArgs, + "spec", "kubeadmConfigSpec", "clusterConfiguration", "controllerManager", "extraArgs") + addArgsV1Beta1(managedFieldSet, kcp.Spec.KubeadmConfigSpec.ClusterConfiguration.Scheduler.ExtraArgs, + "spec", "kubeadmConfigSpec", "clusterConfiguration", "scheduler", "extraArgs") + if kcp.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local != nil { + addArgsV1Beta1(managedFieldSet, kcp.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local.ExtraArgs, + "spec", "kubeadmConfigSpec", "clusterConfiguration", "etcd", "local", "extraArgs") + } + } + if kcp.Spec.KubeadmConfigSpec.InitConfiguration != nil { + addArgsV1Beta1(managedFieldSet, kcp.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.KubeletExtraArgs, + "spec", "kubeadmConfigSpec", "initConfiguration", "nodeRegistration", "kubeletExtraArgs") + } + if kcp.Spec.KubeadmConfigSpec.JoinConfiguration != nil { + addArgsV1Beta1(managedFieldSet, kcp.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.KubeletExtraArgs, + "spec", "kubeadmConfigSpec", "joinConfiguration", "nodeRegistration", "kubeletExtraArgs") + } + } + + if managedFieldSet.Empty() { + // Add a seeding managedFields entry to ensure apiserver does not add a before-first-apply + // managedFields entry during the next SSA call. + // More specifically, if an existing object doesn't have managedFields when applying the next SSA + // the API server creates a before-first-apply entry. This entry then ends up acting as a co-ownership + // and we want to prevent this (because co-ownership prevents us from removing fields). + // NOTE: fieldV1Map cannot be empty, so we add metadata.name which will be cleaned up at the first + // SSA patch of the same fieldManager. + managedFieldSet.Insert(fieldpath.MakePathOrDie("metadata", "name")) + } + + fieldsV1, err := managedFieldSet.ToJSON() + if err != nil { + return nil, errors.Wrap(err, "failed to marshal managedFields entry") + } + + return fieldsV1, nil +} + +func isManager(manager string) func(entry metav1.ManagedFieldsEntry) bool { + return func(entry metav1.ManagedFieldsEntry) bool { + return entry.Manager == manager + } +} + +func addArgs(managedFieldSet *fieldpath.Set, args []bootstrapv1.Arg, fields ...any) { + if len(args) == 0 { + return + } + + for _, arg := range args { + managedFieldSet.Insert(fieldpath.MakePathOrDie(append(fields, fieldpath.KeyByFields("name", arg.Name, "value", ptr.Deref(arg.Value, "")))...)) + managedFieldSet.Insert(fieldpath.MakePathOrDie(append(fields, fieldpath.KeyByFields("name", arg.Name, "value", ptr.Deref(arg.Value, "")), "name")...)) + managedFieldSet.Insert(fieldpath.MakePathOrDie(append(fields, fieldpath.KeyByFields("name", arg.Name, "value", ptr.Deref(arg.Value, "")), "value")...)) + } +} + +func addArgsV1Beta1(managedFieldSet *fieldpath.Set, args map[string]string, fields ...any) { + if len(args) == 0 { + return + } + + for argName := range args { + managedFieldSet.Insert(fieldpath.MakePathOrDie(append(fields, argName)...)) + } +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/matchers.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/matchers.go deleted file mode 100644 index 93dd8e1147..0000000000 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/matchers.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -Copyright 2023 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 ssa - -import ( - "encoding/json" - "fmt" - - "github.com/onsi/gomega/types" - "github.com/pkg/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "sigs.k8s.io/cluster-api/internal/contract" -) - -// MatchManagedFieldsEntry is a gomega Matcher to check if a ManagedFieldsEntry has the given name and operation. -func MatchManagedFieldsEntry(manager string, operation metav1.ManagedFieldsOperationType) types.GomegaMatcher { - return &managedFieldMatcher{ - manager: manager, - operation: operation, - } -} - -type managedFieldMatcher struct { - manager string - operation metav1.ManagedFieldsOperationType -} - -func (mf *managedFieldMatcher) Match(actual interface{}) (bool, error) { - managedFieldsEntry, ok := actual.(metav1.ManagedFieldsEntry) - if !ok { - return false, fmt.Errorf("expecting metav1.ManagedFieldsEntry got %T", actual) - } - - return managedFieldsEntry.Manager == mf.manager && managedFieldsEntry.Operation == mf.operation, nil -} - -func (mf *managedFieldMatcher) FailureMessage(actual interface{}) string { - managedFieldsEntry := actual.(metav1.ManagedFieldsEntry) - return fmt.Sprintf("Expected ManagedFieldsEntry to match Manager:%s and Operation:%s, got Manager:%s, Operation:%s", - mf.manager, mf.operation, managedFieldsEntry.Manager, managedFieldsEntry.Operation) -} - -func (mf *managedFieldMatcher) NegatedFailureMessage(actual interface{}) string { - managedFieldsEntry := actual.(metav1.ManagedFieldsEntry) - return fmt.Sprintf("Expected ManagedFieldsEntry to not match Manager:%s and Operation:%s, got Manager:%s, Operation:%s", - mf.manager, mf.operation, managedFieldsEntry.Manager, managedFieldsEntry.Operation) -} - -// MatchFieldOwnership is a gomega Matcher to check if path is owned by the given manager and operation. -// Note: The path has to be specified as is observed in managed fields. Example: to check if the labels are owned -// by the correct manager the correct way to pass the path is contract.Path{"f:metadata","f:labels"}. -func MatchFieldOwnership(manager string, operation metav1.ManagedFieldsOperationType, path contract.Path) types.GomegaMatcher { - return &fieldOwnershipMatcher{ - path: path, - manager: manager, - operation: operation, - } -} - -type fieldOwnershipMatcher struct { - path contract.Path - manager string - operation metav1.ManagedFieldsOperationType -} - -func (fom *fieldOwnershipMatcher) Match(actual interface{}) (bool, error) { - managedFields, ok := actual.([]metav1.ManagedFieldsEntry) - if !ok { - return false, fmt.Errorf("expecting []metav1.ManagedFieldsEntry got %T", actual) - } - for _, managedFieldsEntry := range managedFields { - if managedFieldsEntry.Manager == fom.manager && managedFieldsEntry.Operation == fom.operation { - fieldsV1 := map[string]interface{}{} - if err := json.Unmarshal(managedFieldsEntry.FieldsV1.Raw, &fieldsV1); err != nil { - return false, errors.Wrap(err, "failed to parse managedFieldsEntry.FieldsV1") - } - FilterIntent(&FilterIntentInput{ - Path: contract.Path{}, - Value: fieldsV1, - ShouldFilter: IsPathNotAllowed([]contract.Path{fom.path}), - }) - return len(fieldsV1) > 0, nil - } - } - return false, nil -} - -func (fom *fieldOwnershipMatcher) FailureMessage(actual interface{}) string { - managedFields := actual.([]metav1.ManagedFieldsEntry) - return fmt.Sprintf("Expected Path %s to be owned by Manager:%s and Operation:%s, did not find correct ownership: %s", - fom.path, fom.manager, fom.operation, managedFields) -} - -func (fom *fieldOwnershipMatcher) NegatedFailureMessage(actual interface{}) string { - managedFields := actual.([]metav1.ManagedFieldsEntry) - return fmt.Sprintf("Expected Path %s to not be owned by Manager:%s and Operation:%s, did not find correct ownership: %s", - fom.path, fom.manager, fom.operation, managedFields) -} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/patch.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/patch.go index ef42bbaac5..eefb0c4d11 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/patch.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/util/ssa/patch.go @@ -35,6 +35,14 @@ type Option interface { ApplyToOptions(*Options) } +// WithDryRun enables the DryRunAll option. +type WithDryRun struct{} + +// ApplyToOptions applies WithDryRun to the given Options. +func (w WithDryRun) ApplyToOptions(in *Options) { + in.WithDryRun = true +} + // WithCachingProxy enables caching for the patch request. // The original and modified object will be used to generate an // identifier for the request. @@ -53,6 +61,7 @@ func (w WithCachingProxy) ApplyToOptions(in *Options) { // Options contains the options for the Patch func. type Options struct { + WithDryRun bool WithCachingProxy bool Cache Cache Original client.Object @@ -75,6 +84,7 @@ func Patch(ctx context.Context, c client.Client, fieldManager string, modified c if err != nil { return err } + modifiedUnstructuredBeforeApply := modifiedUnstructured.DeepCopy() gvk, err := apiutil.GVKForObject(modifiedUnstructured, c.Scheme()) if err != nil { @@ -84,7 +94,7 @@ func Patch(ctx context.Context, c client.Client, fieldManager string, modified c var requestIdentifier string if options.WithCachingProxy { // Check if the request is cached. - requestIdentifier, err = ComputeRequestIdentifier(c.Scheme(), options.Original, modifiedUnstructured) + requestIdentifier, err = ComputeRequestIdentifier(c.Scheme(), options.Original.GetResourceVersion(), modifiedUnstructured) if err != nil { return errors.Wrapf(err, "failed to apply object") } @@ -99,12 +109,18 @@ func Patch(ctx context.Context, c client.Client, fieldManager string, modified c } } - patchOptions := []client.PatchOption{ + applyOptions := []client.ApplyOption{ client.ForceOwnership, client.FieldOwner(fieldManager), } - if err := c.Patch(ctx, modifiedUnstructured, client.Apply, patchOptions...); err != nil { - return errors.Wrapf(err, "failed to apply %s %s", gvk.Kind, klog.KObj(modifiedUnstructured)) + if options.WithDryRun { + applyOptions = append(applyOptions, client.DryRunAll) + } + // Note: Intentionally not including the name of the object in the error message + // as during create the name might be random generated in every reconcile. + // If these errors are written to conditions this would lead to an infinite reconcile. + if err := c.Apply(ctx, client.ApplyConfigurationFromUnstructured(modifiedUnstructured), applyOptions...); err != nil { + return errors.Wrapf(err, "failed to apply %s", gvk.Kind) } // Write back the modified object so callers can access the patched object. @@ -115,11 +131,18 @@ func Patch(ctx context.Context, c client.Client, fieldManager string, modified c // Recover gvk e.g. for logging. modified.GetObjectKind().SetGroupVersionKind(gvk) - if options.WithCachingProxy { - // If the SSA call did not update the object, add the request to the cache. - if options.Original.GetResourceVersion() == modifiedUnstructured.GetResourceVersion() { - options.Cache.Add(requestIdentifier) + // Add the request to the cache only if dry-run was not used. + if options.WithCachingProxy && !options.WithDryRun { + // If the object changed, we need to recompute the request identifier before caching. + if options.Original.GetResourceVersion() != modifiedUnstructured.GetResourceVersion() { + // NOTE: This uses the resourceVersion from modifiedUnstructured (after apply), and the hash from + // modifiedUnstructuredBeforeApply (what we wanted to apply), which is what we want. + requestIdentifier, err = ComputeRequestIdentifier(c.Scheme(), modifiedUnstructured.GetResourceVersion(), modifiedUnstructuredBeforeApply) + if err != nil { + return errors.Wrapf(err, "failed to compute request identifier after apply") + } } + options.Cache.Add(requestIdentifier) } return nil @@ -128,7 +151,6 @@ func Patch(ctx context.Context, c client.Client, fieldManager string, modified c // prepareModified converts obj into an Unstructured and filters out undesired fields. func prepareModified(scheme *runtime.Scheme, obj client.Object) (*unstructured.Unstructured, error) { u := &unstructured.Unstructured{} - dropEmptyStructAndNil := false switch obj.(type) { case *unstructured.Unstructured: u = obj.DeepCopyObject().(*unstructured.Unstructured) @@ -136,9 +158,6 @@ func prepareModified(scheme *runtime.Scheme, obj client.Object) (*unstructured.U if err := scheme.Convert(obj, u, nil); err != nil { return nil, errors.Wrap(err, "failed to convert object to Unstructured") } - // NOTE: DropEmptyStructAndNil is required for typed objects, because they are converted to unstructured using the DefaultUnstructuredConverter, - // and it does not handle omitzero (yet). - dropEmptyStructAndNil = true } // Only keep the paths that we have opinions on. @@ -158,7 +177,6 @@ func prepareModified(scheme *runtime.Scheme, obj client.Object) (*unstructured.U {"metadata", "ownerReferences"}, {"spec"}, }, - DropEmptyStructAndNil: dropEmptyStructAndNil, }) return u, nil } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/cluster.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/cluster.go index 706334dcbd..c4ab372ecc 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/cluster.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/cluster.go @@ -29,6 +29,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" kerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/wait" @@ -38,7 +39,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" + runtimev1 "sigs.k8s.io/cluster-api/api/runtime/v1beta2" "sigs.k8s.io/cluster-api/controllers/external" + runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/contract" "sigs.k8s.io/cluster-api/internal/topology/check" @@ -79,8 +83,6 @@ type Cluster struct { var _ webhook.CustomDefaulter = &Cluster{} var _ webhook.CustomValidator = &Cluster{} -var errClusterClassNotReconciled = errors.New("ClusterClass is not successfully reconciled") - // Default satisfies the defaulting webhook interface. func (webhook *Cluster) Default(ctx context.Context, obj runtime.Object) error { // We gather all defaulting errors and return them together. @@ -109,14 +111,15 @@ func (webhook *Cluster) Default(ctx context.Context, obj runtime.Object) error { return apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("Cluster").GroupKind(), cluster.Name, allErrs) } - clusterClass, err := webhook.pollClusterClassForCluster(ctx, cluster) + clusterClass, clusterClassNotReconciled, clusterClassNotFound, err := webhook.pollClusterClassForCluster(ctx, cluster) if err != nil { - // If the ClusterClass can't be found or is not up to date ignore the error. - if apierrors.IsNotFound(err) || errors.Is(err, errClusterClassNotReconciled) { - return nil - } return apierrors.NewInternalError(errors.Wrapf(err, "Cluster %s can't be defaulted. ClusterClass %s can not be retrieved", cluster.Name, cluster.GetClassKey().Name)) } + if clusterClassNotReconciled || clusterClassNotFound { + // If the ClusterClass can't be found or is not reconciled, return as we shouldn't + // default and validate variables in that case. + return nil + } // Validate cluster class variables transitions that may be enforced by CEL validation rules on variables. // If no request found in context, then this has not come via a webhook request, so skip validation of old cluster. @@ -292,6 +295,8 @@ func (webhook *Cluster) validateTopology(ctx context.Context, oldCluster, newClu // metadata in topology should be valid allErrs = append(allErrs, validateTopologyMetadata(newCluster.Spec.Topology, fldPath)...) + allErrs = append(allErrs, validateTopologyRollout(newCluster.Spec.Topology, fldPath)...) + // upgrade concurrency should be a numeric value. if concurrency, ok := newCluster.Annotations[clusterv1.ClusterTopologyUpgradeConcurrencyAnnotation]; ok { concurrencyAnnotationField := field.NewPath("metadata", "annotations", clusterv1.ClusterTopologyUpgradeConcurrencyAnnotation) @@ -312,39 +317,36 @@ func (webhook *Cluster) validateTopology(ctx context.Context, oldCluster, newClu } // Get the ClusterClass referenced in the Cluster. - clusterClass, warnings, clusterClassPollErr := webhook.validateClusterClassExistsAndIsReconciled(ctx, newCluster) - // If the error is anything other than "NotFound" or "NotReconciled" return all errors. - if clusterClassPollErr != nil && (!apierrors.IsNotFound(clusterClassPollErr) && !errors.Is(clusterClassPollErr, errClusterClassNotReconciled)) { - allErrs = append( - allErrs, field.InternalError( - fldPath.Child("class"), - clusterClassPollErr)) - return allWarnings, allErrs + // Note: If the ClusterClass is not found, a warning and no err is returned and the ClusterClass is nil. + // Note: If the ClusterClass is not reconciled, a warning and no err is returned and the ClusterClass is returned. + clusterClass, warnings, err := webhook.validateClusterClassExistsAndIsReconciled(ctx, newCluster) + if err != nil { + return allWarnings, append(allErrs, field.InternalError(fldPath.Child("class"), err)) } - - // Add the warnings if no error was returned. allWarnings = append(allWarnings, warnings...) - // If there's no error validate the Cluster based on the ClusterClass. - if clusterClassPollErr == nil { + // If we could get the ClusterClass validate the Cluster based on the ClusterClass. + if clusterClass != nil { allErrs = append(allErrs, ValidateClusterForClusterClass(newCluster, clusterClass)...) } // Validate the Cluster and associated ClusterClass' autoscaler annotations. + // Note: ClusterClass validation is only run if clusterClass is not nil. allErrs = append(allErrs, validateAutoscalerAnnotationsForCluster(newCluster, clusterClass)...) if oldCluster != nil { // On update - // The ClusterClass must exist to proceed with update validation. Return an error if the ClusterClass was - // not found. - if apierrors.IsNotFound(clusterClassPollErr) { + // The ClusterClass must exist to proceed with update validation as in that case the ClusterClass + // should always be there (vs. during create it could be racy if Cluster and ClusterClass are created + // at the same time). Return an error if the ClusterClass was not found. + if clusterClass == nil { allErrs = append( allErrs, field.InternalError( fldPath.Child("class"), - clusterClassPollErr)) + errors.Errorf("ClusterClass %s not found", newCluster.GetClassKey()))) return allWarnings, allErrs } - // Topology or Class can not be added on update unless ClusterTopologyUnsafeUpdateClassNameAnnotation is set. + // Topology or Class can not be added or update unless ClusterTopologyUnsafeUpdateClassNameAnnotation is set. if !oldCluster.Spec.Topology.IsDefined() || oldCluster.GetClassKey().Name == "" { if _, ok := newCluster.Annotations[clusterv1.ClusterTopologyUnsafeUpdateClassNameAnnotation]; ok { return allWarnings, allErrs @@ -391,36 +393,39 @@ func (webhook *Cluster) validateTopology(ctx context.Context, oldCluster, newClu log.Info(warningMsg) allWarnings = append(allWarnings, warningMsg) } else { - if err := webhook.validateTopologyVersion(ctx, fldPath.Child("version"), newCluster.Spec.Topology.Version, inVersion, oldVersion, oldCluster); err != nil { + // NOTE: Validate the version ceiling only if: + // * there are no Kubernetes versions defined in the ClusterClass and + // * there is no generateUpgradePlan extension defined in the ClusterClass + // + // If there are Kubernetes versions defined, we will instead validate that the Cluster.spec.topology.version + // is one of these versions and then we can use the chained upgrade feature to upgrade to that version. + // Note: The ClusterClass webhook ensures the KubernetesVersions in the ClusterClass don't have any gaps. + // + // If a generateUpgradePlan extension is defined, we assume that additionally a Cluster validating webhook is implemented + // that validates Cluster.spec.topology.version in a way that matches with GenerateUpgradePlan responses. + shouldValidateVersionCeiling := len(clusterClass.Spec.KubernetesVersions) == 0 && clusterClass.Spec.Upgrade.External.GenerateUpgradePlanExtension == "" + if err := webhook.validateTopologyVersionUpdate(ctx, fldPath.Child("version"), newCluster.Spec.Topology.Version, inVersion, oldVersion, newCluster, oldCluster, shouldValidateVersionCeiling); err != nil { allErrs = append(allErrs, err) } } // If the ClusterClass referenced in the Topology has changed compatibility checks are needed. if oldCluster.GetClassKey() != newCluster.GetClassKey() { - if clusterClassPollErr != nil { - allErrs = append( - allErrs, field.Forbidden( - fldPath.Child("class"), - fmt.Sprintf("cannot rebase to ClusterClass %q: %s", - newCluster.GetClassKey(), clusterClassPollErr.Error()))) - // Return early with errors if the new ClusterClass can't be retrieved. - return allWarnings, allErrs - } - // Check to see if the ClusterClass referenced in the old version of the Cluster exists. - oldClusterClass, err := webhook.pollClusterClassForCluster(ctx, oldCluster) - if err != nil { + // Return early with errors if the old ClusterClass can't be retrieved. + oldClusterClass := &clusterv1.ClusterClass{} + if err := webhook.Client.Get(ctx, oldCluster.GetClassKey(), oldClusterClass); err != nil { allErrs = append( allErrs, field.Forbidden( fldPath.Child("class"), fmt.Sprintf("valid ClusterClass with name %q could not be retrieved, change from class %[1]q to class %q cannot be validated. Error: %s", oldCluster.GetClassKey(), newCluster.GetClassKey(), err.Error()))) - - // Return early with errors if the old ClusterClass can't be retrieved. return allWarnings, allErrs } + // Note: We don't care if the old ClusterClass is reconciled as the validation below doesn't need it + // and we want to allow to rebase away from a broken ClusterClass. + // Check if the new and old ClusterClasses are compatible with one another. allErrs = append(allErrs, check.ClusterClassesAreCompatible(oldClusterClass, clusterClass)...) } @@ -429,7 +434,7 @@ func (webhook *Cluster) validateTopology(ctx context.Context, oldCluster, newClu return allWarnings, allErrs } -func (webhook *Cluster) validateTopologyVersion(ctx context.Context, fldPath *field.Path, fldValue string, inVersion, oldVersion semver.Version, oldCluster *clusterv1.Cluster) *field.Error { +func (webhook *Cluster) validateTopologyVersionUpdate(ctx context.Context, fldPath *field.Path, fldValue string, inVersion, oldVersion semver.Version, newCluster, oldCluster *clusterv1.Cluster, shouldValidateCeiling bool) *field.Error { // Nothing to do if the version doesn't change. if inVersion.String() == oldVersion.String() { return nil @@ -444,17 +449,28 @@ func (webhook *Cluster) validateTopologyVersion(ctx context.Context, fldPath *fi ) } - // A +2 minor version upgrade is not allowed. - ceilVersion := semver.Version{ - Major: oldVersion.Major, - Minor: oldVersion.Minor + 2, - Patch: 0, + if shouldValidateCeiling { + // A +2 minor version upgrade is not allowed. + ceilVersion := semver.Version{ + Major: oldVersion.Major, + Minor: oldVersion.Minor + 2, + Patch: 0, + } + if version.Compare(inVersion, ceilVersion, version.WithoutPreReleases()) >= 0 { + return field.Invalid( + fldPath, + fldValue, + fmt.Sprintf("version cannot be increased from %q to %q", oldVersion, inVersion), + ) + } } - if version.Compare(inVersion, ceilVersion, version.WithoutPreReleases()) >= 0 { + + // Cannot upgrade when lifecycle hooks are still being completed for the previous upgrade. + if IsPending(runtimehooksv1.AfterClusterUpgrade, newCluster) { return field.Invalid( fldPath, fldValue, - fmt.Sprintf("version cannot be increased from %q to %q", oldVersion, inVersion), + fmt.Sprintf("version cannot be changed when the %q hook is still blocking", runtimecatalog.HookName(runtimehooksv1.AfterClusterUpgrade)), ) } @@ -633,11 +649,11 @@ func validateTopologyMachinePoolVersions(ctx context.Context, ctrlClient client. return nil } -func validateClusterRollout(cluster *clusterv1.Cluster) field.ErrorList { +func validateTopologyRollout(topology clusterv1.Topology, fldPath *field.Path) field.ErrorList { var allErrs field.ErrorList - for _, md := range cluster.Spec.Topology.Workers.MachineDeployments { - fldPath := field.NewPath("spec", "topology", "workers", "machineDeployments").Key(md.Name).Child("rollout") + for _, md := range topology.Workers.MachineDeployments { + fldPath := fldPath.Child("workers", "machineDeployments").Key(md.Name).Child("rollout") allErrs = append(allErrs, validateRolloutStrategy(fldPath.Child("strategy"), md.Rollout.Strategy.RollingUpdate.MaxUnavailable, md.Rollout.Strategy.RollingUpdate.MaxSurge)...) } @@ -888,12 +904,29 @@ func ValidateClusterForClusterClass(cluster *clusterv1.Cluster, clusterClass *cl if clusterClass == nil { return field.ErrorList{field.InternalError(field.NewPath(""), errors.New("ClusterClass can not be nil"))} } + + // If the ClusterClass defines a list of versions, check the version is one of them. + if len(clusterClass.Spec.KubernetesVersions) > 0 { + found := false + for _, clusterClassVersion := range clusterClass.Spec.KubernetesVersions { + if clusterClassVersion == cluster.Spec.Topology.Version { + found = true + break + } + } + if !found { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec", "topology", "version"), + cluster.Spec.Topology.Version, + "version must match one of the versions defined in the ClusterClass", + )) + } + } + allErrs = append(allErrs, check.MachineDeploymentTopologiesAreValidAndDefinedInClusterClass(cluster, clusterClass)...) allErrs = append(allErrs, check.MachinePoolTopologiesAreValidAndDefinedInClusterClass(cluster, clusterClass)...) - allErrs = append(allErrs, validateClusterRollout(cluster)...) - // Validate the MachineHealthChecks defined in the cluster topology. allErrs = append(allErrs, validateMachineHealthChecks(cluster, clusterClass)...) return allErrs @@ -903,38 +936,37 @@ func ValidateClusterForClusterClass(cluster *clusterv1.Cluster, clusterClass *cl // In any other case it will return an error. func (webhook *Cluster) validateClusterClassExistsAndIsReconciled(ctx context.Context, newCluster *clusterv1.Cluster) (*clusterv1.ClusterClass, admission.Warnings, error) { var allWarnings admission.Warnings - clusterClass, clusterClassPollErr := webhook.pollClusterClassForCluster(ctx, newCluster) - if clusterClassPollErr != nil { - // Add a warning if the Class does not exist or if it has not been successfully reconciled. - switch { - case apierrors.IsNotFound(clusterClassPollErr): - allWarnings = append(allWarnings, - fmt.Sprintf( - "Cluster refers to ClusterClass %s, but this ClusterClass does not exist. "+ - "Cluster topology has not been fully validated. "+ - "The ClusterClass must be created to reconcile the Cluster", newCluster.GetClassKey()), - ) - case errors.Is(clusterClassPollErr, errClusterClassNotReconciled): - allWarnings = append(allWarnings, - fmt.Sprintf( - "Cluster refers to ClusterClass %s, but this ClusterClass hasn't been successfully reconciled. "+ - "Cluster topology has not been fully validated. "+ - "Please take a look at the ClusterClass status", newCluster.GetClassKey()), - ) - // If there's any other error return a generic warning with the error message. - default: - allWarnings = append(allWarnings, - fmt.Sprintf( - "Cluster refers to ClusterClass %s, but this ClusterClass could not be retrieved. "+ - "Cluster topology has not been fully validated: %s", newCluster.GetClassKey(), clusterClassPollErr.Error()), - ) - } + clusterClass, clusterClassNotReconciled, clusterClassNotFound, err := webhook.pollClusterClassForCluster(ctx, newCluster) + // Add a warning if the Class does not exist or if it has not been successfully reconciled. + switch { + case err != nil: + allWarnings = append(allWarnings, + fmt.Sprintf( + "Cluster refers to ClusterClass %s, but this ClusterClass could not be retrieved. "+ + "Cluster topology has not been fully validated: %s", newCluster.GetClassKey(), err.Error()), + ) + case clusterClassNotFound: + allWarnings = append(allWarnings, + fmt.Sprintf( + "Cluster refers to ClusterClass %s, but this ClusterClass does not exist. "+ + "Cluster topology has not been fully validated. "+ + "The ClusterClass must be created to reconcile the Cluster", newCluster.GetClassKey()), + ) + case clusterClassNotReconciled: + allWarnings = append(allWarnings, + fmt.Sprintf( + "Cluster refers to ClusterClass %s, but this ClusterClass hasn't been successfully reconciled. "+ + "Cluster topology has not been fully validated. "+ + "Please take a look at the ClusterClass status", newCluster.GetClassKey()), + ) } - return clusterClass, allWarnings, clusterClassPollErr + return clusterClass, allWarnings, err } // pollClusterClassForCluster will retry getting the ClusterClass referenced in the Cluster for two seconds. -func (webhook *Cluster) pollClusterClassForCluster(ctx context.Context, cluster *clusterv1.Cluster) (*clusterv1.ClusterClass, error) { +func (webhook *Cluster) pollClusterClassForCluster(ctx context.Context, cluster *clusterv1.Cluster) (_ *clusterv1.ClusterClass, clusterClassNotReconciled, clusterClassNotFound bool, _ error) { + var errClusterClassNotReconciled = errors.New("ClusterClass is not successfully reconciled") + clusterClass := &clusterv1.ClusterClass{} var clusterClassPollErr error _ = wait.PollUntilContextTimeout(ctx, 200*time.Millisecond, 2*time.Second, true, func(ctx context.Context) (bool, error) { @@ -942,31 +974,40 @@ func (webhook *Cluster) pollClusterClassForCluster(ctx context.Context, cluster return false, nil //nolint:nilerr } - if clusterClassPollErr = clusterClassIsReconciled(clusterClass); clusterClassPollErr != nil { - return false, nil //nolint:nilerr + if !clusterClassIsReconciled(clusterClass) { + clusterClassPollErr = errClusterClassNotReconciled + return false, nil } + clusterClassPollErr = nil return true, nil }) if clusterClassPollErr != nil { - return nil, clusterClassPollErr + if apierrors.IsNotFound(clusterClassPollErr) { + return nil, false, true, nil + } + if errors.Is(clusterClassPollErr, errClusterClassNotReconciled) { + // Return ClusterClass if we were able to get it and it's just not reconciled. + return clusterClass, true, false, nil + } + return nil, false, false, clusterClassPollErr } - return clusterClass, nil + return clusterClass, false, false, nil } // clusterClassIsReconciled returns errClusterClassNotReconciled if the ClusterClass has not successfully reconciled or if the // ClusterClass variables have not been successfully reconciled. -func clusterClassIsReconciled(clusterClass *clusterv1.ClusterClass) error { +func clusterClassIsReconciled(clusterClass *clusterv1.ClusterClass) bool { // If the clusterClass metadata generation does not match the status observed generation, the ClusterClass has not been successfully reconciled. if clusterClass.Generation != clusterClass.Status.ObservedGeneration { - return errClusterClassNotReconciled + return false } // If the clusterClass does not have ClusterClassVariablesReconciled==True, the ClusterClass has not been successfully reconciled. if !conditions.Has(clusterClass, clusterv1.ClusterClassVariablesReadyCondition) || conditions.IsFalse(clusterClass, clusterv1.ClusterClassVariablesReadyCondition) { - return errClusterClassNotReconciled + return false } - return nil + return true } func validateTopologyMetadata(topology clusterv1.Topology, fldPath *field.Path) field.ErrorList { @@ -1042,3 +1083,27 @@ func validateAutoscalerAnnotationsForCluster(cluster *clusterv1.Cluster, cluster } return allErrs } + +// Note: code duplicated from internal/hooks/tracking.go to avoid a circular dependency when running tests +// # sigs.k8s.io/cluster-api/util/patch +// package sigs.k8s.io/cluster-api/util/patch +// imports sigs.k8s.io/cluster-api/internal/test/envtest from suite_test.go +// imports sigs.k8s.io/cluster-api/internal/webhooks from environment.go +// imports sigs.k8s.io/cluster-api/internal/hooks from cluster.go +// imports sigs.k8s.io/cluster-api/util/patch from tracking.go: import cycle not allowed in test +// TODO: investigate. + +// IsPending returns true if there is an intent to call a hook being tracked in the object's PendingHooksAnnotation. +func IsPending(hook runtimecatalog.Hook, obj client.Object) bool { + hookName := runtimecatalog.HookName(hook) + annotations := obj.GetAnnotations() + if annotations == nil { + return false + } + return isInCommaSeparatedList(annotations[runtimev1.PendingHooksAnnotation], hookName) +} + +func isInCommaSeparatedList(list, item string) bool { + set := sets.Set[string]{}.Insert(strings.Split(list, ",")...) + return set.Has(item) +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterclass.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterclass.go index d3d7185992..8bba1ae801 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterclass.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterclass.go @@ -21,6 +21,7 @@ import ( "fmt" "strings" + "github.com/blang/semver/v4" "github.com/pkg/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -40,6 +41,7 @@ import ( topologynames "sigs.k8s.io/cluster-api/internal/topology/names" "sigs.k8s.io/cluster-api/internal/topology/variables" clog "sigs.k8s.io/cluster-api/util/log" + "sigs.k8s.io/cluster-api/util/version" ) func (webhook *ClusterClass) SetupWebhookWithManager(mgr ctrl.Manager) error { @@ -145,6 +147,9 @@ func (webhook *ClusterClass) validate(ctx context.Context, oldClusterClass, newC // Validate metadata allErrs = append(allErrs, validateClusterClassMetadata(newClusterClass)...) + // Ensure all kubernetes versions are valid. + allErrs = append(allErrs, validateKubernetesVersions(newClusterClass.Spec.KubernetesVersions)...) + // If this is an update run additional validation. if oldClusterClass != nil { // Ensure spec changes are compatible. @@ -158,6 +163,10 @@ func (webhook *ClusterClass) validate(ctx context.Context, oldClusterClass, newC return apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("ClusterClass").GroupKind(), newClusterClass.Name, allErrs) } + // Ensure New ClusterClass contains Kubernetes versions of all the Clusters of ClusterClass. + allErrs = append(allErrs, + webhook.validateKubernetesVersionsOfClusters(clusters, oldClusterClass, newClusterClass)...) + // Ensure no MachineDeploymentClass currently in use has been removed from the ClusterClass. allErrs = append(allErrs, webhook.validateRemovedMachineDeploymentClassesAreNotUsed(clusters, oldClusterClass, newClusterClass)...) @@ -241,6 +250,32 @@ func validateUpdatesToMachineHealthCheckClasses(clusters []clusterv1.Cluster, ol return allErrs } +func (webhook *ClusterClass) validateKubernetesVersionsOfClusters(clusters []clusterv1.Cluster, _, newClusterClass *clusterv1.ClusterClass) field.ErrorList { + var allErrs field.ErrorList + + // If there is no KubernetesVersions is set in the ClusterClass return early. + if len(newClusterClass.Spec.KubernetesVersions) == 0 { + return allErrs + } + + kubernetesVersions := sets.Set[string]{} + for _, v := range newClusterClass.Spec.KubernetesVersions { + kubernetesVersions.Insert(v) + } + + // Error if any Cluster's Kubernetes version is not set in the ClusterClass. + for _, c := range clusters { + if !kubernetesVersions.Has(c.Spec.Topology.Version) { + allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "kubernetesVersions"), + fmt.Sprintf("Kubernetes Version %s is used by Cluster %q but not set in ClusterClass", + c.Spec.Topology.Version, c.Name), + )) + } + } + + return allErrs +} + func (webhook *ClusterClass) validateRemovedMachineDeploymentClassesAreNotUsed(clusters []clusterv1.Cluster, oldClusterClass, newClusterClass *clusterv1.ClusterClass) field.ErrorList { var allErrs field.ErrorList @@ -501,3 +536,40 @@ func validateAutoscalerAnnotationsForClusterClass(clusters []clusterv1.Cluster, } return allErrs } + +// validateKubernetesVersions iterates over a list of versions and check they are valid. +func validateKubernetesVersions(versions []string) field.ErrorList { + var allErrs field.ErrorList + var previousVersion *semver.Version + for i, v := range versions { + semV, err := semver.ParseTolerant(v) + if err != nil { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec", "kubernetesVersion").Index(i), + v, + "version must be a valid semantic version", + )) + continue + } + if previousVersion != nil { + // Note: we tolerate having one version followed by another with the same major.minor.patch but different build tags (version.Compare==2) + if version.Compare(semV, *previousVersion, version.WithBuildTags()) <= 0 { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec", "kubernetesVersion").Index(i), + v, + fmt.Sprintf("version must be greater than v%s", previousVersion.String()), + )) + } + + if previousVersion.Minor != semV.Minor && previousVersion.Minor+1 != semV.Minor { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec", "kubernetesVersion").Index(i), + v, + fmt.Sprintf("expecting a version with minor %d or %d, found version %s", previousVersion.Minor, previousVersion.Minor+1, semV), + )) + } + } + previousVersion = &semV + } + return allErrs +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterresourceset_webhook.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterresourceset.go similarity index 93% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterresourceset_webhook.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterresourceset.go index 6a8ca18c45..fca3d45540 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterresourceset_webhook.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterresourceset.go @@ -30,7 +30,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" addonsv1 "sigs.k8s.io/cluster-api/api/addons/v1beta2" - "sigs.k8s.io/cluster-api/feature" ) // ClusterResourceSet implements a validation and defaulting webhook for ClusterResourceSet. @@ -93,15 +92,6 @@ func (webhook *ClusterResourceSet) ValidateDelete(_ context.Context, _ runtime.O func (webhook *ClusterResourceSet) validate(oldCRS, newCRS *addonsv1.ClusterResourceSet) error { var allErrs field.ErrorList - // NOTE: ClusterResourceSet is behind ClusterResourceSet feature gate flag; the web hook - // must prevent creating new objects when the feature flag is disabled. - if !feature.Gates.Enabled(feature.ClusterResourceSet) { - return field.Forbidden( - field.NewPath("spec"), - "can be set only if the ClusterResourceSet feature flag is enabled", - ) - } - // Validate selector parses as Selector selector, err := metav1.LabelSelectorAsSelector(&newCRS.Spec.ClusterSelector) if err != nil { diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterresourcesetbinding_webhook.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterresourcesetbinding.go similarity index 90% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterresourcesetbinding_webhook.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterresourcesetbinding.go index cca0aef051..2a1dcb7697 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterresourcesetbinding_webhook.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/clusterresourcesetbinding.go @@ -28,7 +28,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" addonsv1 "sigs.k8s.io/cluster-api/api/addons/v1beta2" - "sigs.k8s.io/cluster-api/feature" ) func (webhook *ClusterResourceSetBinding) SetupWebhookWithManager(mgr ctrl.Manager) error { @@ -75,15 +74,6 @@ func (webhook *ClusterResourceSetBinding) ValidateDelete(_ context.Context, _ ru func (webhook *ClusterResourceSetBinding) validate(oldCRSB, newCRSB *addonsv1.ClusterResourceSetBinding) error { var allErrs field.ErrorList - // NOTE: ClusterResourceSet is behind ClusterResourceSet feature gate flag; the web hook - // must prevent creating new objects in case the feature flag is disabled. - if !feature.Gates.Enabled(feature.ClusterResourceSet) { - return field.Forbidden( - field.NewPath("spec"), - "can be set only if the ClusterResourceSet feature flag is enabled", - ) - } - if oldCRSB != nil && oldCRSB.Spec.ClusterName != "" && oldCRSB.Spec.ClusterName != newCRSB.Spec.ClusterName { allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "clusterName"), newCRSB.Spec.ClusterName, "field is immutable")) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/runtime/extensionconfig_webhook.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/extensionconfig.go similarity index 99% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/runtime/extensionconfig_webhook.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/extensionconfig.go index 33c030ff6a..ac8390de88 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/runtime/extensionconfig_webhook.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/extensionconfig.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package runtime +package webhooks import ( "context" diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/internal/webhooks/ipaddress.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/ipaddress.go similarity index 100% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/internal/webhooks/ipaddress.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/ipaddress.go diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/internal/webhooks/ipaddressclaim.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/ipaddressclaim.go similarity index 100% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/ipam/internal/webhooks/ipaddressclaim.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/ipaddressclaim.go diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machine.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machine.go index bcd434fce5..f7863180cc 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machine.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machine.go @@ -30,6 +30,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/util/labels" "sigs.k8s.io/cluster-api/util/version" ) @@ -136,8 +137,72 @@ func (webhook *Machine) validate(oldM, newM *clusterv1.Machine) error { } } + allErrs = append(allErrs, validateMachineTaints(newM.Spec.Taints, specPath.Child("taints"))...) + allErrs = append(allErrs, validateMachineTaintsForWorkers(newM.Spec.Taints, newM, specPath.Child("taints"))...) + if len(allErrs) == 0 { return nil } return apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("Machine").GroupKind(), newM.Name, allErrs) } + +func validateMachineTaints(taints []clusterv1.MachineTaint, taintsPath *field.Path) field.ErrorList { + var allErrs field.ErrorList + + if !feature.Gates.Enabled(feature.MachineTaintPropagation) { + if len(taints) > 0 { + allErrs = append(allErrs, field.Forbidden(taintsPath, "taints are not allowed to be set when the feature gate MachineTaintPropagation is disabled")) + } + } + + for i, taint := range taints { + idxPath := taintsPath.Index(i) + + // The following validations uses a switch statement, because if one of them matches, then the others won't. + + switch { + // Validate for keys which are reserved for usage by the cluster-api or providers. + case taint.Key == clusterv1.NodeUninitializedTaint.Key: + allErrs = append(allErrs, field.Invalid(idxPath.Child("key"), taint.Key, "taint key is not allowed")) + case taint.Key == clusterv1.NodeOutdatedRevisionTaint.Key: + allErrs = append(allErrs, field.Invalid(idxPath.Child("key"), taint.Key, "taint key is not allowed")) + // Validate for keys which are reserved for usage by the node or node-lifecycle-controller, but allow `node.kubernetes.io/out-of-service`. + case strings.HasPrefix(taint.Key, "node.kubernetes.io/") && taint.Key != "node.kubernetes.io/out-of-service": + allErrs = append(allErrs, field.Invalid(idxPath.Child("key"), taint.Key, "taint key must not have the prefix node.kubernetes.io/, except for node.kubernetes.io/out-of-service")) + // Validate for keys which are reserved for usage by the cloud-controller-manager or kubelet. + case strings.HasPrefix(taint.Key, "node.cloudprovider.kubernetes.io/"): + allErrs = append(allErrs, field.Invalid(idxPath.Child("key"), taint.Key, "taint key must not have the prefix node.cloudprovider.kubernetes.io/")) + // Validate for the deprecated kubeadm node-role taint. + case taint.Key == "node-role.kubernetes.io/master": + allErrs = append(allErrs, field.Invalid(idxPath.Child("key"), taint.Key, "taint is deprecated since 1.24 and should not be used anymore")) + } + } + + return allErrs +} + +func validateMachineTaintsForWorkers(taints []clusterv1.MachineTaint, machine *clusterv1.Machine, taintsPath *field.Path) field.ErrorList { + var allErrs field.ErrorList + + if !feature.Gates.Enabled(feature.MachineTaintPropagation) { + return allErrs + } + + // Skip for control-plane machines. + if machine != nil && machine.Labels != nil { + if _, ok := machine.Labels[clusterv1.MachineControlPlaneLabel]; ok { + return allErrs + } + } + + // Validate taints for worker machines. + for i, taint := range taints { + idxPath := taintsPath.Index(i) + + if taint.Key == "node-role.kubernetes.io/control-plane" { + allErrs = append(allErrs, field.Invalid(idxPath.Child("key"), taint.Key, "taint is not allowed for worker Machines")) + } + } + + return allErrs +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machinedeployment.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machinedeployment.go index dadec0df3e..40e3ef90f5 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machinedeployment.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machinedeployment.go @@ -254,6 +254,9 @@ func (webhook *MachineDeployment) validate(oldMD, newMD *clusterv1.MachineDeploy allErrs = append(allErrs, validateMDMachineNaming(newMD.Spec.MachineNaming, specPath.Child("machineNaming"))...) + allErrs = append(allErrs, validateMachineTaints(newMD.Spec.Template.Spec.Taints, specPath.Child("template", "spec", "taints"))...) + allErrs = append(allErrs, validateMachineTaintsForWorkers(newMD.Spec.Template.Spec.Taints, nil, specPath.Child("template", "spec", "taints"))...) + // Validate the metadata of the template. allErrs = append(allErrs, newMD.Spec.Template.Validate(specPath.Child("template", "metadata"))...) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/webhooks/machinepool.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machinepool.go similarity index 96% rename from cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/webhooks/machinepool.go rename to cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machinepool.go index 9b440eeca5..631e42e5bb 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/exp/internal/webhooks/machinepool.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machinepool.go @@ -37,8 +37,6 @@ import ( "sigs.k8s.io/cluster-api/util/version" ) -const defaultNodeDeletionTimeoutSeconds = int32(10) - func (webhook *MachinePool) SetupWebhookWithManager(mgr ctrl.Manager) error { if webhook.decoder == nil { webhook.decoder = admission.NewDecoder(mgr.GetScheme()) @@ -191,6 +189,13 @@ func (webhook *MachinePool) validate(oldObj, newObj *clusterv1.MachinePool) erro } } + if len(newObj.Spec.Template.Spec.Taints) > 0 { + allErrs = append(allErrs, field.Forbidden(specPath.Child("taints"), "taints feature for MachinePools is not yet implemented")) + if !feature.Gates.Enabled(feature.MachineTaintPropagation) { + allErrs = append(allErrs, field.Forbidden(specPath.Child("taints"), "taints are not allowed to be set when the feature gate MachineTaintPropagation is disabled")) + } + } + // Validate the metadata of the MachinePool template. allErrs = append(allErrs, newObj.Spec.Template.Validate(specPath.Child("template", "metadata"))...) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machineset.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machineset.go index 856108b02b..75d011a2d2 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machineset.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/machineset.go @@ -229,6 +229,9 @@ func (webhook *MachineSet) validate(oldMS, newMS *clusterv1.MachineSet) error { } } + allErrs = append(allErrs, validateMachineTaints(newMS.Spec.Template.Spec.Taints, specPath.Child("template", "spec", "taints"))...) + allErrs = append(allErrs, validateMachineTaintsForWorkers(newMS.Spec.Template.Spec.Taints, nil, specPath.Child("template", "spec", "taints"))...) + allErrs = append(allErrs, validateMSMachineNaming(newMS.Spec.MachineNaming, specPath.Child("machineNaming"))...) // Validate the metadata of the template. diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/patch_validation.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/patch_validation.go index 56af76c6d4..a1d447bbb7 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/patch_validation.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/patch_validation.go @@ -331,12 +331,12 @@ func validateJSONPatches(jsonPatches []clusterv1.JSONPatch, variables []clusterv )) } - if !strings.HasPrefix(jsonPatch.Path, "/spec/") { + if !strings.HasPrefix(jsonPatch.Path, "/spec") { allErrs = append(allErrs, field.Invalid( path.Index(i).Child("path"), prettyPrint(jsonPatch), - "jsonPatch path must start with \"/spec/\"", + "jsonPatch path must start with \"/spec\"", )) } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/runtime/doc.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/runtime/doc.go deleted file mode 100644 index 6bf9f066c8..0000000000 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/internal/webhooks/runtime/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2022 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 runtime contains the webhook implementation for runtime ExtensionConfig. -package runtime diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/main.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/main.go index acecd3ffbe..272c192f54 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/main.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/main.go @@ -49,6 +49,7 @@ import ( "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/certwatcher" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/config" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -69,12 +70,8 @@ import ( "sigs.k8s.io/cluster-api/controllers/clustercache" "sigs.k8s.io/cluster-api/controllers/crdmigrator" "sigs.k8s.io/cluster-api/controllers/remote" - expcontrollers "sigs.k8s.io/cluster-api/exp/controllers" - expipamwebhooks "sigs.k8s.io/cluster-api/exp/ipam/webhooks" runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" runtimeclient "sigs.k8s.io/cluster-api/exp/runtime/client" - runtimecontrollers "sigs.k8s.io/cluster-api/exp/runtime/controllers" - expwebhooks "sigs.k8s.io/cluster-api/exp/webhooks" "sigs.k8s.io/cluster-api/feature" addonsv1alpha3 "sigs.k8s.io/cluster-api/internal/api/addons/v1alpha3" addonsv1alpha4 "sigs.k8s.io/cluster-api/internal/api/addons/v1alpha4" @@ -543,13 +540,25 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchNamespaces map var runtimeClient runtimeclient.Client if feature.Gates.Enabled(feature.RuntimeSDK) { // This is the creation of the runtimeClient for the controllers, embedding a shared catalog and registry instance. - runtimeClient = internalruntimeclient.New(internalruntimeclient.Options{ + var certWatcher *certwatcher.CertWatcher + runtimeClient, certWatcher, err = internalruntimeclient.New(internalruntimeclient.Options{ CertFile: runtimeExtensionCertFile, KeyFile: runtimeExtensionKeyFile, Catalog: catalog, Registry: runtimeregistry.New(), Client: mgr.GetClient(), }) + if err != nil { + setupLog.Error(err, "Unable to create RuntimeSDK client") + os.Exit(1) + } + if certWatcher != nil { + // Note: certWatcher is managed by the manager to ensure that a certWatcher failure leads to a binary restart. + if err := mgr.Add(certWatcher); err != nil { + setupLog.Error(err, "Unable to add RuntimeSDK client cert-watcher to the manager") + os.Exit(1) + } + } } // Setup a separate cache without label selector for secrets, to be used @@ -623,12 +632,13 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchNamespaces map } if feature.Gates.Enabled(feature.RuntimeSDK) { - if err = (&runtimecontrollers.ExtensionConfigReconciler{ - Client: mgr.GetClient(), - APIReader: mgr.GetAPIReader(), - RuntimeClient: runtimeClient, - WatchFilterValue: watchFilterValue, - }).SetupWithManager(ctx, mgr, concurrency(extensionConfigConcurrency), partialSecretCache); err != nil { + if err = (&controllers.ExtensionConfigReconciler{ + Client: mgr.GetClient(), + APIReader: mgr.GetAPIReader(), + RuntimeClient: runtimeClient, + PartialSecretCache: partialSecretCache, + WatchFilterValue: watchFilterValue, + }).SetupWithManager(ctx, mgr, concurrency(extensionConfigConcurrency)); err != nil { setupLog.Error(err, "Unable to create controller", "controller", "ExtensionConfig") os.Exit(1) } @@ -675,6 +685,7 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchNamespaces map Client: mgr.GetClient(), APIReader: mgr.GetAPIReader(), ClusterCache: clusterCache, + RuntimeClient: runtimeClient, WatchFilterValue: watchFilterValue, RemoteConditionsGracePeriod: remoteConditionsGracePeriod, AdditionalSyncMachineLabels: additionalSyncMachineLabelRegexes, @@ -715,6 +726,7 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchNamespaces map if err := (&controllers.MachineDeploymentReconciler{ Client: mgr.GetClient(), APIReader: mgr.GetAPIReader(), + RuntimeClient: runtimeClient, WatchFilterValue: watchFilterValue, }).SetupWithManager(ctx, mgr, concurrency(machineDeploymentConcurrency)); err != nil { setupLog.Error(err, "Unable to create controller", "controller", "MachineDeployment") @@ -722,7 +734,7 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchNamespaces map } if feature.Gates.Enabled(feature.MachinePool) { - if err := (&expcontrollers.MachinePoolReconciler{ + if err := (&controllers.MachinePoolReconciler{ Client: mgr.GetClient(), APIReader: mgr.GetAPIReader(), ClusterCache: clusterCache, @@ -733,22 +745,20 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchNamespaces map } } - if feature.Gates.Enabled(feature.ClusterResourceSet) { - if err := (&controllers.ClusterResourceSetReconciler{ - Client: mgr.GetClient(), - ClusterCache: clusterCache, - WatchFilterValue: watchFilterValue, - }).SetupWithManager(ctx, mgr, concurrency(clusterResourceSetConcurrency), partialSecretCache); err != nil { - setupLog.Error(err, "Unable to create controller", "controller", "ClusterResourceSet") - os.Exit(1) - } - if err := (&controllers.ClusterResourceSetBindingReconciler{ - Client: mgr.GetClient(), - WatchFilterValue: watchFilterValue, - }).SetupWithManager(ctx, mgr, concurrency(clusterResourceSetConcurrency)); err != nil { - setupLog.Error(err, "Unable to create controller", "controller", "ClusterResourceSetBinding") - os.Exit(1) - } + if err := (&controllers.ClusterResourceSetReconciler{ + Client: mgr.GetClient(), + ClusterCache: clusterCache, + WatchFilterValue: watchFilterValue, + }).SetupWithManager(ctx, mgr, concurrency(clusterResourceSetConcurrency), partialSecretCache); err != nil { + setupLog.Error(err, "Unable to create controller", "controller", "ClusterResourceSet") + os.Exit(1) + } + if err := (&controllers.ClusterResourceSetBindingReconciler{ + Client: mgr.GetClient(), + WatchFilterValue: watchFilterValue, + }).SetupWithManager(ctx, mgr, concurrency(clusterResourceSetConcurrency)); err != nil { + setupLog.Error(err, "Unable to create controller", "controller", "ClusterResourceSetBinding") + os.Exit(1) } if err := (&controllers.MachineHealthCheckReconciler{ @@ -808,7 +818,7 @@ func setupWebhooks(ctx context.Context, mgr ctrl.Manager, clusterCacheReader web // NOTE: MachinePool is behind MachinePool feature gate flag; the webhook // is going to prevent creating or updating new objects in case the feature flag is disabled - if err := (&expwebhooks.MachinePool{}).SetupWebhookWithManager(mgr); err != nil { + if err := (&webhooks.MachinePool{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "Unable to create webhook", "webhook", "MachinePool") os.Exit(1) } @@ -838,14 +848,14 @@ func setupWebhooks(ctx context.Context, mgr ctrl.Manager, clusterCacheReader web os.Exit(1) } - if err := (&expipamwebhooks.IPAddress{ + if err := (&webhooks.IPAddress{ // We are using GetAPIReader here to avoid caching all IPAddressClaims Client: mgr.GetAPIReader(), }).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "Unable to create webhook", "webhook", "IPAddress") os.Exit(1) } - if err := (&expipamwebhooks.IPAddressClaim{}).SetupWebhookWithManager(mgr); err != nil { + if err := (&webhooks.IPAddressClaim{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "Unable to create webhook", "webhook", "IPAddressClaim") os.Exit(1) } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/metadata.yaml b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/metadata.yaml index 4d94191c95..8abce47eb8 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/metadata.yaml +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/metadata.yaml @@ -6,6 +6,9 @@ apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3 kind: Metadata releaseSeries: + - major: 1 + minor: 12 + contract: v1beta2 - major: 1 minor: 11 contract: v1beta2 diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/netlify.toml b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/netlify.toml index c52c15af9d..5f5446eddf 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/netlify.toml +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/netlify.toml @@ -4,7 +4,7 @@ publish = "docs/book/book" [build.environment] - GO_VERSION = "1.24.9" + GO_VERSION = "1.24.13" # Standard Netlify redirects [[redirects]] diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/cache/cache.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/cache/cache.go index 353ea170d3..fca15809d0 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/cache/cache.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/cache/cache.go @@ -17,18 +17,23 @@ limitations under the License. package cache import ( + "fmt" "time" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" kcache "k8s.io/client-go/tools/cache" - ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" + runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" ) const ( // DefaultTTL is the duration for which we keep entries in the cache. DefaultTTL = 10 * time.Minute + // HookCacheDefaultTTL is the duration for which we keep entries in the hook cache. + HookCacheDefaultTTL = 24 * time.Hour + // expirationInterval is the interval in which we will remove expired entries // from the cache. expirationInterval = 10 * time.Hour @@ -49,6 +54,12 @@ type Cache[E Entry] interface { // Has checks if the given key (still) exists in the Cache. // Note: entries expire after the ttl. Has(key string) (E, bool) + + // Len returns the number of entries in the cache. + Len() int + + // DeleteAll deletes all entries from the cache. + DeleteAll() } // New creates a new cache. @@ -96,47 +107,50 @@ func (r *cache[E]) Has(key string) (E, bool) { return *new(E), false } -// ReconcileEntry is an Entry for the Cache that stores the -// earliest time after which the next Reconcile should be executed. -type ReconcileEntry struct { - Request ctrl.Request - ReconcileAfter time.Time +func (r *cache[E]) Len() int { + return len(r.ListKeys()) +} + +func (r *cache[E]) DeleteAll() { + // Note: We are intentionally using Replace instead of List + Delete because the latter would be racy. + _ = r.Store.Replace(nil, "") } -// NewReconcileEntry creates a new ReconcileEntry based on an object and a reconcileAfter time. -func NewReconcileEntry(obj metav1.Object, reconcileAfter time.Time) ReconcileEntry { - return ReconcileEntry{ - Request: ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: obj.GetNamespace(), - Name: obj.GetName(), - }, - }, - ReconcileAfter: reconcileAfter, +// HookEntry is an Entry for the hook Cache. +type HookEntry struct { + ObjectKey client.ObjectKey + HookName string + ReconcileAfter time.Time + ResponseMessage string +} + +// NewHookEntry creates a new HookEntry based on an object and a reconcileAfter time. +func NewHookEntry(obj client.Object, hook runtimecatalog.Hook, reconcileAfter time.Time, responseMessage string) HookEntry { + return HookEntry{ + ObjectKey: client.ObjectKeyFromObject(obj), + HookName: runtimecatalog.HookName(hook), + ReconcileAfter: reconcileAfter, + ResponseMessage: responseMessage, } } -// NewReconcileEntryKey returns the key of a ReconcileEntry based on an object. -func NewReconcileEntryKey(obj metav1.Object) string { - return ReconcileEntry{ - Request: ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: obj.GetNamespace(), - Name: obj.GetName(), - }, - }, +// NewHookEntryKey returns the key of a HookEntry based on an object. +func NewHookEntryKey(obj client.Object, hook runtimecatalog.Hook) string { + return HookEntry{ + ObjectKey: client.ObjectKeyFromObject(obj), + HookName: runtimecatalog.HookName(hook), }.Key() } -var _ Entry = &ReconcileEntry{} +var _ Entry = &HookEntry{} -// Key returns the cache key of a ReconcileEntry. -func (r ReconcileEntry) Key() string { - return r.Request.String() +// Key returns the cache key of a HookEntry. +func (r HookEntry) Key() string { + return fmt.Sprintf("%s: %s", r.HookName, r.ObjectKey.String()) } // ShouldRequeue returns if the current Reconcile should be requeued. -func (r ReconcileEntry) ShouldRequeue(now time.Time) (requeueAfter time.Duration, requeue bool) { +func (r HookEntry) ShouldRequeue(now time.Time) (requeueAfter time.Duration, requeue bool) { if r.ReconcileAfter.IsZero() { return time.Duration(0), false } @@ -147,3 +161,16 @@ func (r ReconcileEntry) ShouldRequeue(now time.Time) (requeueAfter time.Duration return time.Duration(0), false } + +// ToResponse uses the values from a HookEntry to set status, message and retryAfterSeconds on a RetryResponseObject. +// requeueAfter is passed in to make it possible to use durations computed from previous ShouldRequeue calls. +func (r HookEntry) ToResponse(resp runtimehooksv1.RetryResponseObject, requeueAfter time.Duration) runtimehooksv1.RetryResponseObject { + resp.SetStatus(runtimehooksv1.ResponseStatusSuccess) + resp.SetMessage(r.ResponseMessage) + if requeueAfter > 0 { + // Note: We have to set at least 1 if requeueAfter > 0 otherwise components like + // the HookResponseTracker won't detect this correctly as a blocking response. + resp.SetRetryAfterSeconds(max(int32(requeueAfter.Round(time.Second).Seconds()), 1)) + } + return resp +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/certs/certs.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/certs/certs.go index 225438bb4e..71c6e2436a 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/certs/certs.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/certs/certs.go @@ -19,13 +19,18 @@ package certs import ( "crypto" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/pem" + "fmt" "github.com/pkg/errors" kerrors "k8s.io/apimachinery/pkg/util/errors" + + bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" ) // NewPrivateKey creates an RSA private key. @@ -113,3 +118,72 @@ func DecodePrivateKeyPEM(encoded []byte) (crypto.Signer, error) { return nil, kerrors.NewAggregate(errs) } + +// NewSigner creates a private key based on the provided encryption key algorithm. +func NewSigner(keyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType) (crypto.Signer, error) { + switch keyEncryptionAlgorithm { + case bootstrapv1.EncryptionAlgorithmECDSAP256: + return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case bootstrapv1.EncryptionAlgorithmECDSAP384: + return ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + } + rsaKeySize := rsaKeySizeFromAlgorithmType(keyEncryptionAlgorithm) + if rsaKeySize == 0 { + return nil, errors.Errorf("cannot obtain key size from unknown RSA algorithm: %q", keyEncryptionAlgorithm) + } + return rsa.GenerateKey(rand.Reader, rsaKeySize) +} + +// EncodePrivateKeyPEMFromSigner converts a known private key type of RSA or ECDSA to +// a PEM encoded block or returns an error. +func EncodePrivateKeyPEMFromSigner(key crypto.PrivateKey) ([]byte, error) { + switch t := key.(type) { + case *ecdsa.PrivateKey: + derBytes, err := x509.MarshalECPrivateKey(t) + if err != nil { + return nil, err + } + block := &pem.Block{ + Type: "EC PRIVATE KEY", + Bytes: derBytes, + } + return pem.EncodeToMemory(block), nil + case *rsa.PrivateKey: + block := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(t), + } + return pem.EncodeToMemory(block), nil + default: + return nil, fmt.Errorf("private key is not a recognized type: %T", key) + } +} + +// EncodePublicKeyPEMFromSigner returns PEM-encoded public key data. +func EncodePublicKeyPEMFromSigner(key crypto.PublicKey) ([]byte, error) { + der, err := x509.MarshalPKIXPublicKey(key) + if err != nil { + return []byte{}, err + } + block := pem.Block{ + Type: "PUBLIC KEY", + Bytes: der, + } + return pem.EncodeToMemory(&block), nil +} + +// rsaKeySizeFromAlgorithmType takes a known RSA algorithm defined in the kubeadm API and returns its key size. +// For unknown types it returns 0. +// For an empty type ("") which is the default (zero value) on the API field it returns the default size of 2048. +func rsaKeySizeFromAlgorithmType(keyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType) int { + switch keyEncryptionAlgorithm { + case bootstrapv1.EncryptionAlgorithmRSA2048, "": + return 2048 + case bootstrapv1.EncryptionAlgorithmRSA3072: + return 3072 + case bootstrapv1.EncryptionAlgorithmRSA4096: + return 4096 + default: + return 0 + } +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/certs/types.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/certs/types.go index 6039846872..462b781b7f 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/certs/types.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/certs/types.go @@ -19,7 +19,6 @@ package certs import ( "crypto" "crypto/rand" - "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "math" @@ -49,7 +48,7 @@ type Config struct { } // NewSignedCert creates a signed certificate using the given CA certificate and key. -func (cfg *Config) NewSignedCert(key *rsa.PrivateKey, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) { +func (cfg *Config) NewSignedCert(key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) { serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) if err != nil { return nil, errors.Wrap(err, "failed to generate random integer for signed cerficate") diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/collections/machine_filters.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/collections/machine_filters.go index 4f7340622c..4efea929a2 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/collections/machine_filters.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/collections/machine_filters.go @@ -22,6 +22,7 @@ import ( "github.com/blang/semver/v4" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/selection" "sigs.k8s.io/controller-runtime/pkg/client" @@ -31,7 +32,7 @@ import ( "sigs.k8s.io/cluster-api/util/conditions" ) -// Func is the functon definition for a filter. +// Func is the function definition for a filter. type Func func(machine *clusterv1.Machine) bool // And returns a filter that returns true if all of the given filters returns true. @@ -100,13 +101,13 @@ func InFailureDomains(failureDomains ...string) Func { } // OwnedMachines returns a filter to find all machines owned by specified owner. -// Usage: GetFilteredMachinesForCluster(ctx, client, cluster, OwnedMachines(controlPlane)). -func OwnedMachines(owner client.Object) func(machine *clusterv1.Machine) bool { +// Usage: GetFilteredMachinesForCluster(ctx, client, cluster, OwnedMachines(controlPlane, controlPlaneGK)). +func OwnedMachines(owner client.Object, ownerGK schema.GroupKind) func(machine *clusterv1.Machine) bool { return func(machine *clusterv1.Machine) bool { if machine == nil { return false } - return util.IsOwnedByObject(machine, owner) + return util.IsOwnedByObject(machine, owner, ownerGK) } } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/conditions/setter.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/conditions/setter.go index fcea28476a..376c2c0890 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/conditions/setter.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/conditions/setter.go @@ -116,7 +116,9 @@ func Delete(to Setter, conditionType string) { } conditions := to.GetConditions() - newConditions := make([]metav1.Condition, 0, len(conditions)-1) + // allocate same length array because the conditions length might be 0 + // or the condition to be deleted might not be part of the list. + newConditions := make([]metav1.Condition, 0, len(conditions)) for _, condition := range conditions { if condition.Type != conditionType { newConditions = append(newConditions, condition) diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/conditions/sort.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/conditions/sort.go index d0be13af9e..d06baeba82 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/conditions/sort.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/conditions/sort.go @@ -76,6 +76,7 @@ func defaultSortLessFunc(i, j metav1.Condition) bool { // | OwnerRemediated | | | | | | x | // | -- Operations -- | | | | | | | // | TopologyReconciled | x | | | | | | +// | Updating | | | | | | x | // | RollingOut | x | x | x | | x | | // | Remediating | x | x | x | x | x | | // | ScalingDown | x | x | x | x | x | | @@ -117,6 +118,7 @@ var order = []string{ clusterv1.MachineHealthCheckSucceededCondition, clusterv1.MachineOwnerRemediatedCondition, clusterv1.ClusterTopologyReconciledCondition, + clusterv1.MachineUpdatingCondition, clusterv1.RollingOutCondition, clusterv1.RemediatingCondition, clusterv1.ScalingDownCondition, diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/kubeconfig.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/kubeconfig.go index b93c07d03a..a2087f6d1b 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/kubeconfig.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/kubeconfig.go @@ -54,14 +54,17 @@ func FromSecret(ctx context.Context, c client.Reader, cluster client.ObjectKey) } // New creates a new Kubeconfig using the cluster name and specified endpoint. -func New(clusterName, endpoint string, caCert *x509.Certificate, caKey crypto.Signer) (*api.Config, error) { +func New(clusterName, endpoint string, caCert *x509.Certificate, caKey crypto.Signer, options ...KubeConfigOption) (*api.Config, error) { cfg := &certs.Config{ CommonName: "kubernetes-admin", Organization: []string{"system:masters"}, Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, } - clientKey, err := certs.NewPrivateKey() + kubeConfigOptions := &KubeConfigOptions{} + kubeConfigOptions.ApplyOptions(options) + + clientKey, err := certs.NewSigner(kubeConfigOptions.keyEncryptionAlgorithm) if err != nil { return nil, errors.Wrap(err, "unable to create private key") } @@ -71,6 +74,11 @@ func New(clusterName, endpoint string, caCert *x509.Certificate, caKey crypto.Si return nil, errors.Wrap(err, "unable to sign certificate") } + encodedClientKey, err := certs.EncodePrivateKeyPEMFromSigner(clientKey) + if err != nil { + return nil, errors.Wrap(err, "unable to encode private key") + } + userName := fmt.Sprintf("%s-admin", clusterName) contextName := fmt.Sprintf("%s@%s", userName, clusterName) @@ -89,7 +97,7 @@ func New(clusterName, endpoint string, caCert *x509.Certificate, caKey crypto.Si }, AuthInfos: map[string]*api.AuthInfo{ userName: { - ClientKeyData: certs.EncodePrivateKeyPEM(clientKey), + ClientKeyData: encodedClientKey, ClientCertificateData: certs.EncodeCertPEM(clientCert), }, }, @@ -98,23 +106,23 @@ func New(clusterName, endpoint string, caCert *x509.Certificate, caKey crypto.Si } // CreateSecret creates the Kubeconfig secret for the given cluster. -func CreateSecret(ctx context.Context, c client.Client, cluster *clusterv1.Cluster) error { +func CreateSecret(ctx context.Context, c client.Client, cluster *clusterv1.Cluster, options ...KubeConfigOption) error { name := util.ObjectKey(cluster) return CreateSecretWithOwner(ctx, c, name, cluster.Spec.ControlPlaneEndpoint.String(), metav1.OwnerReference{ APIVersion: clusterv1.GroupVersion.String(), Kind: "Cluster", Name: cluster.Name, UID: cluster.UID, - }) + }, options...) } // CreateSecretWithOwner creates the Kubeconfig secret for the given cluster name, namespace, endpoint, and owner reference. -func CreateSecretWithOwner(ctx context.Context, c client.Client, clusterName client.ObjectKey, endpoint string, owner metav1.OwnerReference) error { +func CreateSecretWithOwner(ctx context.Context, c client.Client, clusterName client.ObjectKey, endpoint string, owner metav1.OwnerReference, options ...KubeConfigOption) error { server, err := url.JoinPath("https://", endpoint) if err != nil { return err } - out, err := generateKubeconfig(ctx, c, clusterName, server) + out, err := generateKubeconfig(ctx, c, clusterName, server, options...) if err != nil { return err } @@ -181,7 +189,7 @@ func NeedsClientCertRotation(configSecret *corev1.Secret, threshold time.Duratio } // RegenerateSecret creates and stores a new Kubeconfig in the given secret. -func RegenerateSecret(ctx context.Context, c client.Client, configSecret *corev1.Secret) error { +func RegenerateSecret(ctx context.Context, c client.Client, configSecret *corev1.Secret, options ...KubeConfigOption) error { clusterName, _, err := secret.ParseSecretName(configSecret.Name) if err != nil { return errors.Wrap(err, "failed to parse secret name") @@ -197,7 +205,7 @@ func RegenerateSecret(ctx context.Context, c client.Client, configSecret *corev1 } endpoint := config.Clusters[clusterName].Server key := client.ObjectKey{Name: clusterName, Namespace: configSecret.Namespace} - out, err := generateKubeconfig(ctx, c, key, endpoint) + out, err := generateKubeconfig(ctx, c, key, endpoint, options...) if err != nil { return err } @@ -205,7 +213,7 @@ func RegenerateSecret(ctx context.Context, c client.Client, configSecret *corev1 return c.Update(ctx, configSecret) } -func generateKubeconfig(ctx context.Context, c client.Client, clusterName client.ObjectKey, endpoint string) ([]byte, error) { +func generateKubeconfig(ctx context.Context, c client.Client, clusterName client.ObjectKey, endpoint string, options ...KubeConfigOption) ([]byte, error) { clusterCA, err := secret.GetFromNamespacedName(ctx, c, clusterName, secret.ClusterCA) if err != nil { if apierrors.IsNotFound(err) { @@ -228,7 +236,7 @@ func generateKubeconfig(ctx context.Context, c client.Client, clusterName client return nil, errors.New("CA private key not found") } - cfg, err := New(clusterName.Name, endpoint, cert, key) + cfg, err := New(clusterName.Name, endpoint, cert, key, options...) if err != nil { return nil, errors.Wrap(err, "failed to generate a kubeconfig") } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/options.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/options.go new file mode 100644 index 0000000000..bbeaee3a24 --- /dev/null +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/options.go @@ -0,0 +1,46 @@ +/* +Copyright 2019 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 kubeconfig + +import bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" + +// KubeConfigOption helps to modify KubeConfigOptions. +type KubeConfigOption interface { //nolint:revive + // ApplyKubeConfigOption applies this options to the given KubeConfigOptions options. + ApplyKubeConfigOption(*KubeConfigOptions) +} + +// KubeConfigOptions allows to set options for generating a kubeconfig. +type KubeConfigOptions struct { //nolint:revive + keyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType +} + +// ApplyOptions applies the given list options on these options, +// and then returns itself (for convenient chaining). +func (o *KubeConfigOptions) ApplyOptions(opts []KubeConfigOption) { + for _, opt := range opts { + opt.ApplyKubeConfigOption(o) + } +} + +// KeyEncryptionAlgorithm allows to specify the key encryption algorithm type. +type KeyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType + +// ApplyKubeConfigOption applies this configuration to the given kube configuration options. +func (t KeyEncryptionAlgorithm) ApplyKubeConfigOption(opts *KubeConfigOptions) { + opts.keyEncryptionAlgorithm = bootstrapv1.EncryptionAlgorithmType(t) +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/patch/patch.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/patch/patch.go index 1c994e3b88..64378f457e 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/patch/patch.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/patch/patch.go @@ -143,18 +143,16 @@ func (h *Helper) Patch(ctx context.Context, obj client.Object, opts ...Option) e return errors.Wrapf(err, "failed to patch %s %s: failed to convert after object to Unstructured", h.gvk.Kind, klog.KObj(h.beforeObject)) } - // Determine if the object has status. - if unstructuredHasStatus(h.after) { - if options.IncludeStatusObservedGeneration { - // Set status.observedGeneration if we're asked to do so. - if err := unstructured.SetNestedField(h.after.Object, h.after.GetGeneration(), "status", "observedGeneration"); err != nil { - return errors.Wrapf(err, "failed to patch %s %s: failed to set .status.observedGeneration", h.gvk.Kind, klog.KObj(h.beforeObject)) - } + // Include .status.observedGeneration if IncludeStatusObservedGeneration is set. + if options.IncludeStatusObservedGeneration { + // Set status.observedGeneration if we're asked to do so. + if err := unstructured.SetNestedField(h.after.Object, h.after.GetGeneration(), "status", "observedGeneration"); err != nil { + return errors.Wrapf(err, "failed to patch %s %s: failed to set .status.observedGeneration", h.gvk.Kind, klog.KObj(h.beforeObject)) + } - // Restore the changes back to the original object. - if err := runtime.DefaultUnstructuredConverter.FromUnstructured(h.after.Object, obj); err != nil { - return errors.Wrapf(err, "failed to patch %s %s: failed to converted object from Unstructured", h.gvk.Kind, klog.KObj(h.beforeObject)) - } + // Restore the changes back to the original object. + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(h.after.Object, obj); err != nil { + return errors.Wrapf(err, "failed to patch %s %s: failed to converted object from Unstructured", h.gvk.Kind, klog.KObj(h.beforeObject)) } } @@ -172,17 +170,17 @@ func (h *Helper) Patch(ctx context.Context, obj client.Object, opts ...Option) e // patching conditions first avoids an extra loop if spec or status patch succeeds first // given that causes the resourceVersion to mutate. if err := h.patchStatusConditions(ctx, obj, options.ForceOverwriteConditions, options.OwnedConditions, options.OwnedV1Beta2Conditions); err != nil { - errs = append(errs, err) + errs = append(errs, errors.Wrapf(err, "failed to patch status conditions")) } // Then proceed to patch the rest of the object. if err := h.patch(ctx, obj); err != nil { - errs = append(errs, err) + errs = append(errs, errors.Wrapf(err, "failed to patch spec and metadata")) } if err := h.patchStatus(ctx, obj); err != nil { //nolint:staticcheck if !(apierrors.IsNotFound(err) && !obj.GetDeletionTimestamp().IsZero() && len(obj.GetFinalizers()) == 0) { - errs = append(errs, err) + errs = append(errs, errors.Wrapf(err, "failed to patch status")) } } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/patch/utils.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/patch/utils.go index 460b95a3c6..94d6fbf2a7 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/patch/utils.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/patch/utils.go @@ -43,11 +43,6 @@ var ( } ) -func unstructuredHasStatus(u *unstructured.Unstructured) bool { - _, ok := u.Object["status"] - return ok -} - // toUnstructured converts an object to Unstructured. // We have to pass in a gvk as we can't rely on GVK being set in a runtime.Object. func toUnstructured(obj runtime.Object, gvk schema.GroupVersionKind) (*unstructured.Unstructured, error) { @@ -187,7 +182,7 @@ func identifyConditionsFieldsPath(obj runtime.Object) ([]string, []string, error // We assume the type is implemented according to transition guidelines clusterv1ConditionsFields = []string{"status", "deprecated", "v1beta1", "conditions"} } else { - if v1Beta1Field := deprecatedElem.FieldByName("V1Beta1"); deprecatedField != (reflect.Value{}) { + if v1Beta1Field := deprecatedElem.FieldByName("V1Beta1"); v1Beta1Field != (reflect.Value{}) { if v1Beta1Field.Kind() != reflect.Pointer { return nil, nil, errors.New("obj.status.deprecated.v1beta1 must be a pointer") } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/resource/resource.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/resource/resource.go index 400beb8fde..177f296444 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/resource/resource.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/resource/resource.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package resource implements resource utilites. +// Package resource implements resource utilities. package resource import ( diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/secret/certificates.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/secret/certificates.go index 8af1c12eec..b8dc6405fe 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/secret/certificates.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/secret/certificates.go @@ -18,8 +18,8 @@ package secret import ( "context" + "crypto" "crypto/rand" - "crypto/rsa" "crypto/sha256" "crypto/x509" "crypto/x509/pkix" @@ -69,6 +69,7 @@ type Certificates []*Certificate // NewCertificatesForInitialControlPlane returns a list of certificates configured for a control plane node. func NewCertificatesForInitialControlPlane(config *bootstrapv1.ClusterConfiguration) Certificates { var validityPeriodDays int32 + var keyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType certificatesDir := DefaultCertificatesDir if config != nil { if config.CertificatesDir != "" { @@ -77,34 +78,41 @@ func NewCertificatesForInitialControlPlane(config *bootstrapv1.ClusterConfigurat if config.CACertificateValidityPeriodDays != 0 { validityPeriodDays = config.CACertificateValidityPeriodDays } + if config.EncryptionAlgorithm != "" { + keyEncryptionAlgorithm = config.EncryptionAlgorithm + } } certificates := Certificates{ &Certificate{ - Purpose: ClusterCA, - CertFile: path.Join(certificatesDir, "ca.crt"), - KeyFile: path.Join(certificatesDir, "ca.key"), - ValidityPeriodDays: validityPeriodDays, + Purpose: ClusterCA, + CertFile: path.Join(certificatesDir, "ca.crt"), + KeyFile: path.Join(certificatesDir, "ca.key"), + ValidityPeriodDays: validityPeriodDays, + KeyEncryptionAlgorithm: keyEncryptionAlgorithm, }, &Certificate{ - Purpose: ServiceAccount, - CertFile: path.Join(certificatesDir, "sa.pub"), - KeyFile: path.Join(certificatesDir, "sa.key"), - ValidityPeriodDays: validityPeriodDays, + Purpose: ServiceAccount, + CertFile: path.Join(certificatesDir, "sa.pub"), + KeyFile: path.Join(certificatesDir, "sa.key"), + ValidityPeriodDays: validityPeriodDays, + KeyEncryptionAlgorithm: keyEncryptionAlgorithm, }, &Certificate{ - Purpose: FrontProxyCA, - CertFile: path.Join(certificatesDir, "front-proxy-ca.crt"), - KeyFile: path.Join(certificatesDir, "front-proxy-ca.key"), - ValidityPeriodDays: validityPeriodDays, + Purpose: FrontProxyCA, + CertFile: path.Join(certificatesDir, "front-proxy-ca.crt"), + KeyFile: path.Join(certificatesDir, "front-proxy-ca.key"), + ValidityPeriodDays: validityPeriodDays, + KeyEncryptionAlgorithm: keyEncryptionAlgorithm, }, } etcdCert := &Certificate{ - Purpose: EtcdCA, - CertFile: path.Join(certificatesDir, "etcd", "ca.crt"), - KeyFile: path.Join(certificatesDir, "etcd", "ca.key"), - ValidityPeriodDays: validityPeriodDays, + Purpose: EtcdCA, + CertFile: path.Join(certificatesDir, "etcd", "ca.crt"), + KeyFile: path.Join(certificatesDir, "etcd", "ca.key"), + ValidityPeriodDays: validityPeriodDays, + KeyEncryptionAlgorithm: keyEncryptionAlgorithm, } // TODO make sure all the fields are actually defined and return an error if not @@ -114,13 +122,13 @@ func NewCertificatesForInitialControlPlane(config *bootstrapv1.ClusterConfigurat CertFile: config.Etcd.External.CAFile, External: true, } - apiserverEtcdClientCert := &Certificate{ + apiServerEtcdClientCert := &Certificate{ Purpose: APIServerEtcdClient, CertFile: config.Etcd.External.CertFile, KeyFile: config.Etcd.External.KeyFile, External: true, } - certificates = append(certificates, apiserverEtcdClientCert) + certificates = append(certificates, apiServerEtcdClientCert) } certificates = append(certificates, etcdCert) @@ -331,13 +339,14 @@ func (c Certificates) LookupOrGenerateCached(ctx context.Context, secretCachingC // Certificate represents a single certificate CA. type Certificate struct { - Generated bool - External bool - Purpose Purpose - KeyPair *certs.KeyPair - CertFile, KeyFile string - Secret *corev1.Secret - ValidityPeriodDays int32 + Generated bool + External bool + Purpose Purpose + KeyPair *certs.KeyPair + CertFile, KeyFile string + Secret *corev1.Secret + ValidityPeriodDays int32 + KeyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType } // Hashes hashes all the certificates stored in a CA certificate. @@ -420,7 +429,7 @@ func (c *Certificate) Generate() error { generator = generateServiceAccountKeys } - kp, err := generator(c.ValidityPeriodDays) + kp, err := generator(c.ValidityPeriodDays, c.KeyEncryptionAlgorithm) if err != nil { return err } @@ -473,35 +482,44 @@ func secretToKeyPair(s *corev1.Secret) (*certs.KeyPair, error) { }, nil } -func generateCACert(validityPeriodDays int32) (*certs.KeyPair, error) { - x509Cert, privKey, err := newCertificateAuthority(validityPeriodDays) +func generateCACert(validityPeriodDays int32, keyAlgorithmType bootstrapv1.EncryptionAlgorithmType) (*certs.KeyPair, error) { + x509Cert, privateKey, err := newCertificateAuthority(validityPeriodDays, keyAlgorithmType) + if err != nil { + return nil, err + } + encodedKey, err := certs.EncodePrivateKeyPEMFromSigner(privateKey) if err != nil { return nil, err } return &certs.KeyPair{ Cert: certs.EncodeCertPEM(x509Cert), - Key: certs.EncodePrivateKeyPEM(privKey), + Key: encodedKey, }, nil } -func generateServiceAccountKeys(_ int32) (*certs.KeyPair, error) { - saCreds, err := certs.NewPrivateKey() +func generateServiceAccountKeys(_ int32, keyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType) (*certs.KeyPair, error) { + saCreds, err := certs.NewSigner(keyEncryptionAlgorithm) if err != nil { return nil, err } - saPub, err := certs.EncodePublicKeyPEM(&saCreds.PublicKey) + saPub, err := certs.EncodePublicKeyPEMFromSigner(saCreds.Public()) if err != nil { return nil, err } + saKey, err := certs.EncodePrivateKeyPEMFromSigner(saCreds) + if err != nil { + return nil, err + } + return &certs.KeyPair{ Cert: saPub, - Key: certs.EncodePrivateKeyPEM(saCreds), + Key: saKey, }, nil } // newCertificateAuthority creates new certificate and private key for the certificate authority. -func newCertificateAuthority(validityPeriodDays int32) (*x509.Certificate, *rsa.PrivateKey, error) { - key, err := certs.NewPrivateKey() +func newCertificateAuthority(validityPeriodDays int32, keyAlgorithmType bootstrapv1.EncryptionAlgorithmType) (*x509.Certificate, crypto.Signer, error) { + key, err := certs.NewSigner(keyAlgorithmType) if err != nil { return nil, nil, err } @@ -515,7 +533,7 @@ func newCertificateAuthority(validityPeriodDays int32) (*x509.Certificate, *rsa. } // newSelfSignedCACert creates a CA certificate. -func newSelfSignedCACert(key *rsa.PrivateKey, validityPeriodDays int32) (*x509.Certificate, error) { +func newSelfSignedCACert(key crypto.Signer, validityPeriodDays int32) (*x509.Certificate, error) { cfg := certs.Config{ CommonName: "kubernetes", } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/util.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/util.go index 684ef3d07b..992d574dde 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/util.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/util.go @@ -37,6 +37,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -179,7 +180,7 @@ func GetClusterByName(ctx context.Context, c client.Client, namespace, name stri } if err := c.Get(ctx, key, cluster); err != nil { - return nil, errors.Wrapf(err, "failed to get Cluster/%s", name) + return nil, errors.Wrapf(err, "failed to get Cluster %s", klog.KRef(namespace, name)) } return cluster, nil @@ -359,9 +360,9 @@ func indexOwnerRef(ownerReferences []metav1.OwnerReference, ref metav1.OwnerRefe // IsOwnedByObject returns true if any of the owner references point to the given target. // It matches the object based on the Group, Kind and Name. -func IsOwnedByObject(obj metav1.Object, target client.Object) bool { +func IsOwnedByObject(obj metav1.Object, target client.Object, targetGK schema.GroupKind) bool { for _, ref := range obj.GetOwnerReferences() { - if refersTo(&ref, target) { + if refersTo(&ref, target, targetGK) { return true } } @@ -369,12 +370,12 @@ func IsOwnedByObject(obj metav1.Object, target client.Object) bool { } // IsControlledBy differs from metav1.IsControlledBy. This function matches on Group, Kind and Name. The metav1.IsControlledBy function matches on UID only. -func IsControlledBy(obj metav1.Object, owner client.Object) bool { +func IsControlledBy(obj metav1.Object, owner client.Object, ownerGK schema.GroupKind) bool { controllerRef := metav1.GetControllerOfNoCopy(obj) if controllerRef == nil { return false } - return refersTo(controllerRef, owner) + return refersTo(controllerRef, owner, ownerGK) } // Returns true if a and b point to the same object based on Group, Kind and Name. @@ -393,14 +394,13 @@ func referSameObject(a, b metav1.OwnerReference) bool { } // Returns true if ref refers to obj based on Group, Kind and Name. -func refersTo(ref *metav1.OwnerReference, obj client.Object) bool { +func refersTo(ref *metav1.OwnerReference, obj client.Object, objGK schema.GroupKind) bool { refGv, err := schema.ParseGroupVersion(ref.APIVersion) if err != nil { return false } - gvk := obj.GetObjectKind().GroupVersionKind() - return refGv.Group == gvk.Group && ref.Kind == gvk.Kind && ref.Name == obj.GetName() + return refGv.Group == objGK.Group && ref.Kind == objGK.Kind && ref.Name == obj.GetName() } // UnstructuredUnmarshalField is a wrapper around json and unstructured objects to decode and copy a specific field @@ -690,6 +690,8 @@ func IsSupportedVersionSkew(a, b semver.Version) bool { // LowestNonZeroResult compares two reconciliation results // and returns the one with lowest requeue time. +// +//nolint:staticcheck // SA1019: Requeue is deprecated. func LowestNonZeroResult(i, j ctrl.Result) ctrl.Result { switch { case i.IsZero(): @@ -751,3 +753,89 @@ func MergeMap(maps ...map[string]string) map[string]string { } return m } + +// GetOwnerMachinePool returns the MachinePool objects owning the current resource. +func GetOwnerMachinePool(ctx context.Context, c client.Client, obj metav1.ObjectMeta) (*clusterv1.MachinePool, error) { + for _, ref := range obj.GetOwnerReferences() { + if ref.Kind != "MachinePool" { + continue + } + gv, err := schema.ParseGroupVersion(ref.APIVersion) + if err != nil { + return nil, errors.WithStack(err) + } + if gv.Group == clusterv1.GroupVersion.Group { + return GetMachinePoolByName(ctx, c, obj.Namespace, ref.Name) + } + } + return nil, nil +} + +// GetMachinePoolByName finds and returns a MachinePool object using the specified params. +func GetMachinePoolByName(ctx context.Context, c client.Client, namespace, name string) (*clusterv1.MachinePool, error) { + m := &clusterv1.MachinePool{} + key := client.ObjectKey{Name: name, Namespace: namespace} + if err := c.Get(ctx, key, m); err != nil { + return nil, err + } + return m, nil +} + +// GetMachinePoolByLabels finds and returns a MachinePool object using the value of clusterv1.MachinePoolNameLabel. +// This differs from GetMachinePoolByName as the label value can be a hash. +func GetMachinePoolByLabels(ctx context.Context, c client.Client, namespace string, labels map[string]string) (*clusterv1.MachinePool, error) { + selector := map[string]string{} + if clusterName, ok := labels[clusterv1.ClusterNameLabel]; ok { + selector = map[string]string{clusterv1.ClusterNameLabel: clusterName} + } + + if poolNameHash, ok := labels[clusterv1.MachinePoolNameLabel]; ok { + machinePoolList := &clusterv1.MachinePoolList{} + if err := c.List(ctx, machinePoolList, client.InNamespace(namespace), client.MatchingLabels(selector)); err != nil { + return nil, errors.Wrapf(err, "failed to list MachinePools using labels %v", selector) + } + + for _, mp := range machinePoolList.Items { + if format.MustFormatValue(mp.Name) == poolNameHash { + return &mp, nil + } + } + } else { + return nil, errors.Errorf("labels missing required key `%s`", clusterv1.MachinePoolNameLabel) + } + + return nil, nil +} + +// MachinePoolToInfrastructureMapFunc returns a handler.MapFunc that watches for +// MachinePool events and returns reconciliation requests for an infrastructure provider object. +func MachinePoolToInfrastructureMapFunc(ctx context.Context, gvk schema.GroupVersionKind) handler.MapFunc { + log := ctrl.LoggerFrom(ctx) + return func(_ context.Context, o client.Object) []reconcile.Request { + m, ok := o.(*clusterv1.MachinePool) + if !ok { + log.V(4).Info("Not a machine pool", "Object", klog.KObj(o)) + return nil + } + log := log.WithValues("MachinePool", klog.KObj(o)) + + gk := gvk.GroupKind() + ref := m.Spec.Template.Spec.InfrastructureRef + // Return early if the GroupKind doesn't match what we expect. + infraGK := ref.GroupKind() + if gk != infraGK { + log.V(4).Info("Infra kind doesn't match filter group kind", "infrastructureGroupKind", infraGK.String()) + return nil + } + + log.V(4).Info("Projecting object") + return []reconcile.Request{ + { + NamespacedName: client.ObjectKey{ + Namespace: m.Namespace, + Name: ref.Name, + }, + }, + } + } +} diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/version/version.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/version/version.go index 0e5210b349..132b71ed5d 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/version/version.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/util/version/version.go @@ -130,7 +130,7 @@ func (v buildIdentifiers) compare(o buildIdentifiers) int { } // if everything is equal till now the longer is greater - if i == len(v) && i == len(o) { //nolint: gocritic + if i == len(v) && i == len(o) { return 0 } else if i == len(v) && i < len(o) { return -1 diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/webhooks/alias.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/webhooks/alias.go index a52055a0c2..ff08aaac98 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/webhooks/alias.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/cluster-api/webhooks/alias.go @@ -25,7 +25,6 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/internal/webhooks" - runtimewebhooks "sigs.k8s.io/cluster-api/internal/webhooks/runtime" ) // Cluster implements a validating and defaulting webhook for Cluster. @@ -132,5 +131,34 @@ type ExtensionConfig struct{} // SetupWebhookWithManager sets up ClusterResourceSet webhooks. func (webhook *ExtensionConfig) SetupWebhookWithManager(mgr ctrl.Manager) error { - return (&runtimewebhooks.ExtensionConfig{}).SetupWebhookWithManager(mgr) + return (&webhooks.ExtensionConfig{}).SetupWebhookWithManager(mgr) +} + +// MachinePool implements a validating and defaulting webhook for MachinePool. +type MachinePool struct{} + +// SetupWebhookWithManager sets up MachinePool webhooks. +func (webhook *MachinePool) SetupWebhookWithManager(mgr ctrl.Manager) error { + return (&webhooks.MachinePool{}).SetupWebhookWithManager(mgr) +} + +// IPAddress implements a validating and defaulting webhook for IPAddress. +type IPAddress struct { + Client client.Reader +} + +// SetupWebhookWithManager sets up IPAddress webhooks. +func (webhook *IPAddress) SetupWebhookWithManager(mgr ctrl.Manager) error { + return (&webhooks.IPAddress{ + Client: webhook.Client, + }).SetupWebhookWithManager(mgr) +} + +// IPAddressClaim implements a validating and defaulting webhook for IPAddressClaim. +type IPAddressClaim struct { +} + +// SetupWebhookWithManager sets up IPAddressClaim webhooks. +func (webhook *IPAddressClaim) SetupWebhookWithManager(mgr ctrl.Manager) error { + return (&webhooks.IPAddressClaim{}).SetupWebhookWithManager(mgr) } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/structured-merge-diff/v6/schema/elements.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/structured-merge-diff/v6/schema/elements.go index 5d3707a5b5..c8138a6548 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/structured-merge-diff/v6/schema/elements.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/structured-merge-diff/v6/schema/elements.go @@ -18,6 +18,7 @@ package schema import ( "sync" + "sync/atomic" ) // Schema is a list of named types. @@ -28,7 +29,7 @@ type Schema struct { Types []TypeDef `yaml:"types,omitempty"` once sync.Once - m map[string]TypeDef + m atomic.Pointer[map[string]TypeDef] lock sync.Mutex // Cached results of resolving type references to atoms. Only stores @@ -144,26 +145,28 @@ type Map struct { ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"` once sync.Once - m map[string]StructField + m atomic.Pointer[map[string]StructField] } // FindField is a convenience function that returns the referenced StructField, // if it exists, or (nil, false) if it doesn't. func (m *Map) FindField(name string) (StructField, bool) { m.once.Do(func() { - m.m = make(map[string]StructField, len(m.Fields)) + mm := make(map[string]StructField, len(m.Fields)) for _, field := range m.Fields { - m.m[field.Name] = field + mm[field.Name] = field } + m.m.Store(&mm) }) - sf, ok := m.m[name] + sf, ok := (*m.m.Load())[name] return sf, ok } -// CopyInto this instance of Map into the other -// If other is nil this method does nothing. -// If other is already initialized, overwrites it with this instance -// Warning: Not thread safe +// CopyInto clones this instance of Map into dst +// +// If dst is nil this method does nothing. +// If dst is already initialized, overwrites it with this instance. +// Warning: Not thread safe. Only use dst after this function returns. func (m *Map) CopyInto(dst *Map) { if dst == nil { return @@ -175,12 +178,13 @@ func (m *Map) CopyInto(dst *Map) { dst.Unions = m.Unions dst.ElementRelationship = m.ElementRelationship - if m.m != nil { + mm := m.m.Load() + if mm != nil { // If cache is non-nil then the once token had been consumed. // Must reset token and use it again to ensure same semantics. dst.once = sync.Once{} dst.once.Do(func() { - dst.m = m.m + dst.m.Store(mm) }) } } @@ -274,12 +278,13 @@ type List struct { // if it exists, or (nil, false) if it doesn't. func (s *Schema) FindNamedType(name string) (TypeDef, bool) { s.once.Do(func() { - s.m = make(map[string]TypeDef, len(s.Types)) + sm := make(map[string]TypeDef, len(s.Types)) for _, t := range s.Types { - s.m[t.Name] = t + sm[t.Name] = t } + s.m.Store(&sm) }) - t, ok := s.m[name] + t, ok := (*s.m.Load())[name] return t, ok } @@ -352,10 +357,11 @@ func (s *Schema) Resolve(tr TypeRef) (Atom, bool) { return result, true } -// Clones this instance of Schema into the other -// If other is nil this method does nothing. -// If other is already initialized, overwrites it with this instance -// Warning: Not thread safe +// CopyInto clones this instance of Schema into dst +// +// If dst is nil this method does nothing. +// If dst is already initialized, overwrites it with this instance. +// Warning: Not thread safe. Only use dst after this function returns. func (s *Schema) CopyInto(dst *Schema) { if dst == nil { return @@ -364,12 +370,13 @@ func (s *Schema) CopyInto(dst *Schema) { // Schema type is considered immutable so sharing references dst.Types = s.Types - if s.m != nil { + sm := s.m.Load() + if sm != nil { // If cache is non-nil then the once token had been consumed. // Must reset token and use it again to ensure same semantics. dst.once = sync.Once{} dst.once.Do(func() { - dst.m = s.m + dst.m.Store(sm) }) } } diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/structured-merge-diff/v6/typed/remove.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/structured-merge-diff/v6/typed/remove.go index 86de5105d7..0db1734f94 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/structured-merge-diff/v6/typed/remove.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/structured-merge-diff/v6/typed/remove.go @@ -58,6 +58,10 @@ func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) { defer w.allocator.Free(l) // If list is null or empty just return if l == nil || l.Length() == 0 { + // For extraction, we just return the value as is (which is nil or empty). For extraction the difference matters. + if w.shouldExtract { + w.out = w.value.Unstructured() + } return nil } @@ -71,6 +75,7 @@ func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) { } var newItems []interface{} + hadMatches := false iter := l.RangeUsing(w.allocator) defer w.allocator.Free(iter) for iter.Next() { @@ -80,24 +85,40 @@ func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) { path, _ := fieldpath.MakePath(pe) // save items on the path when we shouldExtract // but ignore them when we are removing (i.e. !w.shouldExtract) - if w.toRemove.Has(path) { - if w.shouldExtract { - newItems = append(newItems, removeItemsWithSchema(item, w.toRemove, w.schema, t.ElementType, w.shouldExtract).Unstructured()) - } else { - continue + isExactPathMatch := w.toRemove.Has(path) + isPrefixMatch := !w.toRemove.WithPrefix(pe).Empty() + if w.shouldExtract { + if isPrefixMatch { + item = removeItemsWithSchema(item, w.toRemove.WithPrefix(pe), w.schema, t.ElementType, w.shouldExtract) + } + if isExactPathMatch || isPrefixMatch { + newItems = append(newItems, item.Unstructured()) } - } - if subset := w.toRemove.WithPrefix(pe); !subset.Empty() { - item = removeItemsWithSchema(item, subset, w.schema, t.ElementType, w.shouldExtract) } else { - // don't save items not on the path when we shouldExtract. - if w.shouldExtract { + if isExactPathMatch { continue } + if isPrefixMatch { + // Removing nested items within this list item and preserve if it becomes empty + hadMatches = true + wasMap := item.IsMap() + wasList := item.IsList() + item = removeItemsWithSchema(item, w.toRemove.WithPrefix(pe), w.schema, t.ElementType, w.shouldExtract) + // If item returned null but we're removing items within the structure(not the item itself), + // preserve the empty container structure + if item.IsNull() && !w.shouldExtract { + if wasMap { + item = value.NewValueInterface(map[string]interface{}{}) + } else if wasList { + item = value.NewValueInterface([]interface{}{}) + } + } + } + newItems = append(newItems, item.Unstructured()) } - newItems = append(newItems, item.Unstructured()) } - if len(newItems) > 0 { + // Preserve empty lists (non-nil) instead of converting to null when items were matched and removed + if len(newItems) > 0 || (hadMatches && !w.shouldExtract) { w.out = newItems } return nil @@ -113,6 +134,10 @@ func (w *removingWalker) doMap(t *schema.Map) ValidationErrors { } // If map is null or empty just return if m == nil || m.Empty() { + // For extraction, we just return the value as is (which is nil or empty). For extraction the difference matters. + if w.shouldExtract { + w.out = w.value.Unstructured() + } return nil } @@ -131,6 +156,7 @@ func (w *removingWalker) doMap(t *schema.Map) ValidationErrors { } newMap := map[string]interface{}{} + hadMatches := false m.Iterate(func(k string, val value.Value) bool { pe := fieldpath.PathElement{FieldName: &k} path, _ := fieldpath.MakePath(pe) @@ -148,7 +174,19 @@ func (w *removingWalker) doMap(t *schema.Map) ValidationErrors { return true } if subset := w.toRemove.WithPrefix(pe); !subset.Empty() { + hadMatches = true + wasMap := val.IsMap() + wasList := val.IsList() val = removeItemsWithSchema(val, subset, w.schema, fieldType, w.shouldExtract) + // If val returned null but we're removing items within the structure (not the field itself), + // preserve the empty container structure + if val.IsNull() && !w.shouldExtract { + if wasMap { + val = value.NewValueInterface(map[string]interface{}{}) + } else if wasList { + val = value.NewValueInterface([]interface{}{}) + } + } } else { // don't save values not on the path when we shouldExtract. if w.shouldExtract { @@ -158,7 +196,8 @@ func (w *removingWalker) doMap(t *schema.Map) ValidationErrors { newMap[k] = val.Unstructured() return true }) - if len(newMap) > 0 { + // Preserve empty maps (non-nil) instead of converting to null when items were matched and removed + if len(newMap) > 0 || (hadMatches && !w.shouldExtract) { w.out = newMap } return nil diff --git a/cluster-api/cluster-api/vendor/sigs.k8s.io/structured-merge-diff/v6/value/reflectcache.go b/cluster-api/cluster-api/vendor/sigs.k8s.io/structured-merge-diff/v6/value/reflectcache.go index 3b4a402ee1..75b7085c3e 100644 --- a/cluster-api/cluster-api/vendor/sigs.k8s.io/structured-merge-diff/v6/value/reflectcache.go +++ b/cluster-api/cluster-api/vendor/sigs.k8s.io/structured-merge-diff/v6/value/reflectcache.go @@ -84,6 +84,10 @@ func (f *FieldCacheEntry) CanOmit(fieldVal reflect.Value) bool { func (f *FieldCacheEntry) GetFrom(structVal reflect.Value) reflect.Value { // field might be nested within 'inline' structs for _, elem := range f.fieldPath { + if safeIsNil(structVal) { + // if any part of the path is nil, return the zero value for the field type + return reflect.Zero(f.fieldType) + } structVal = dereference(structVal).FieldByIndex(elem) } return structVal diff --git a/vendor/go.uber.org/zap/.golangci.yml b/vendor/go.uber.org/zap/.golangci.yml index 2346df1351..74faaa71d8 100644 --- a/vendor/go.uber.org/zap/.golangci.yml +++ b/vendor/go.uber.org/zap/.golangci.yml @@ -25,7 +25,7 @@ linters-settings: govet: # These govet checks are disabled by default, but they're useful. enable: - - niliness + - nilness - reflectvaluecompare - sortslice - unusedwrite diff --git a/vendor/go.uber.org/zap/CHANGELOG.md b/vendor/go.uber.org/zap/CHANGELOG.md index 6d6cd5f4d7..86e7e6f982 100644 --- a/vendor/go.uber.org/zap/CHANGELOG.md +++ b/vendor/go.uber.org/zap/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 1.27.1 (19 Nov 2025) +Enhancements: +* [#1501][]: prevent `Object` from panicking on nils +* [#1511][]: Fix a race condition in `WithLazy`. + +Thanks to @rabbbit, @alshopov, @jquirke, @arukiidou for their contributions to this release. + +[#1501]: https://github.com/uber-go/zap/pull/1501 +[#1511]: https://github.com/uber-go/zap/pull/1511 + ## 1.27.0 (20 Feb 2024) Enhancements: * [#1378][]: Add `WithLazy` method for `SugaredLogger`. diff --git a/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md b/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md index e327d9aa5c..bc988b72ed 100644 --- a/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md +++ b/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md @@ -71,5 +71,5 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]. -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +[homepage]: https://contributor-covenant.org +[version]: https://contributor-covenant.org/version/1/4/ diff --git a/vendor/go.uber.org/zap/LICENSE b/vendor/go.uber.org/zap/LICENSE index 6652bed45f..3883b9a7ea 100644 --- a/vendor/go.uber.org/zap/LICENSE +++ b/vendor/go.uber.org/zap/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2017 Uber Technologies, Inc. +Copyright (c) 2016-2024 Uber Technologies, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/go.uber.org/zap/Makefile b/vendor/go.uber.org/zap/Makefile index eb1cee53bd..f9db385b34 100644 --- a/vendor/go.uber.org/zap/Makefile +++ b/vendor/go.uber.org/zap/Makefile @@ -24,7 +24,7 @@ golangci-lint: @$(foreach mod,$(MODULE_DIRS), \ (cd $(mod) && \ echo "[lint] golangci-lint: $(mod)" && \ - golangci-lint run --path-prefix $(mod)) &&) true + golangci-lint run --path-prefix $(mod) ./...) &&) true .PHONY: tidy tidy: diff --git a/vendor/go.uber.org/zap/field.go b/vendor/go.uber.org/zap/field.go index 6743930b82..1884afabcd 100644 --- a/vendor/go.uber.org/zap/field.go +++ b/vendor/go.uber.org/zap/field.go @@ -398,6 +398,9 @@ func Durationp(key string, val *time.Duration) Field { // struct-like user-defined types to the logging context. The struct's // MarshalLogObject method is called lazily. func Object(key string, val zapcore.ObjectMarshaler) Field { + if val == nil { + return nilField(key) + } return Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val} } @@ -431,6 +434,13 @@ func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } +// DictObject constructs a [zapcore.ObjectMarshaler] with the given list of fields. +// The resulting object marshaler can be used as input to [Object], [Objects], or +// any other functions that expect an object marshaler. +func DictObject(val ...Field) zapcore.ObjectMarshaler { + return dictObject(val) +} + // We discovered an issue where zap.Any can cause a performance degradation // when used in new goroutines. // diff --git a/vendor/go.uber.org/zap/http_handler.go b/vendor/go.uber.org/zap/http_handler.go index 2be8f65150..1cae2c164b 100644 --- a/vendor/go.uber.org/zap/http_handler.go +++ b/vendor/go.uber.org/zap/http_handler.go @@ -71,7 +71,7 @@ import ( func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err := lvl.serveHTTP(w, r); err != nil { w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "internal error: %v", err) + _, _ = fmt.Fprintf(w, "internal error: %v", err) } } diff --git a/vendor/go.uber.org/zap/logger.go b/vendor/go.uber.org/zap/logger.go index c4d3003239..2d0ef141bc 100644 --- a/vendor/go.uber.org/zap/logger.go +++ b/vendor/go.uber.org/zap/logger.go @@ -381,7 +381,11 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { if stack.Count() == 0 { if log.addCaller { - fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", ent.Time.UTC()) + _, _ = fmt.Fprintf( + log.errorOutput, + "%v Logger.check error: failed to get caller\n", + ent.Time.UTC(), + ) _ = log.errorOutput.Sync() } return ce diff --git a/vendor/go.uber.org/zap/options.go b/vendor/go.uber.org/zap/options.go index 43d357ac90..04a3c1e635 100644 --- a/vendor/go.uber.org/zap/options.go +++ b/vendor/go.uber.org/zap/options.go @@ -125,7 +125,11 @@ func IncreaseLevel(lvl zapcore.LevelEnabler) Option { return optionFunc(func(log *Logger) { core, err := zapcore.NewIncreaseLevelCore(log.core, lvl) if err != nil { - fmt.Fprintf(log.errorOutput, "failed to IncreaseLevel: %v\n", err) + _, _ = fmt.Fprintf( + log.errorOutput, + "failed to IncreaseLevel: %v\n", + err, + ) } else { log.core = core } diff --git a/vendor/go.uber.org/zap/sink.go b/vendor/go.uber.org/zap/sink.go index 499772a00d..92202280f1 100644 --- a/vendor/go.uber.org/zap/sink.go +++ b/vendor/go.uber.org/zap/sink.go @@ -71,7 +71,7 @@ func newSinkRegistry() *sinkRegistry { return sr } -// RegisterScheme registers the given factory for the specific scheme. +// RegisterSink registers the given factory for the specific scheme. func (sr *sinkRegistry) RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error { sr.mu.Lock() defer sr.mu.Unlock() diff --git a/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go b/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go index a40e93b3ec..4b426a5648 100644 --- a/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go +++ b/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go @@ -188,32 +188,33 @@ func (s *BufferedWriteSyncer) flushLoop() { // Stop closes the buffer, cleans up background goroutines, and flushes // remaining unwritten data. func (s *BufferedWriteSyncer) Stop() (err error) { - var stopped bool - // Critical section. - func() { + stopped := func() bool { s.mu.Lock() defer s.mu.Unlock() if !s.initialized { - return + return false } - stopped = s.stopped - if stopped { - return + if s.stopped { + return false } s.stopped = true s.ticker.Stop() close(s.stop) // tell flushLoop to stop - <-s.done // and wait until it has + return true }() - // Don't call Sync on consecutive Stops. + // Not initialized, or already stopped, no need for any cleanup. if !stopped { - err = s.Sync() + return } - return err + // Wait for flushLoop to end outside of the lock, as it may need the lock to complete. + // See https://github.com/uber-go/zap/issues/1428 for details. + <-s.done + + return s.Sync() } diff --git a/vendor/go.uber.org/zap/zapcore/console_encoder.go b/vendor/go.uber.org/zap/zapcore/console_encoder.go index cc2b4e07b9..98eea5154d 100644 --- a/vendor/go.uber.org/zap/zapcore/console_encoder.go +++ b/vendor/go.uber.org/zap/zapcore/console_encoder.go @@ -105,7 +105,7 @@ func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, if i > 0 { line.AppendString(c.ConsoleSeparator) } - fmt.Fprint(line, arr.elems[i]) + _, _ = fmt.Fprint(line, arr.elems[i]) } putSliceEncoder(arr) diff --git a/vendor/go.uber.org/zap/zapcore/entry.go b/vendor/go.uber.org/zap/zapcore/entry.go index 459a5d7ce3..841752f2ed 100644 --- a/vendor/go.uber.org/zap/zapcore/entry.go +++ b/vendor/go.uber.org/zap/zapcore/entry.go @@ -241,7 +241,12 @@ func (ce *CheckedEntry) Write(fields ...Field) { // If the entry is dirty, log an internal error; because the // CheckedEntry is being used after it was returned to the pool, // the message may be an amalgamation from multiple call sites. - fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", ce.Time, ce.Entry) + _, _ = fmt.Fprintf( + ce.ErrorOutput, + "%v Unsafe CheckedEntry re-use near Entry %+v.\n", + ce.Time, + ce.Entry, + ) _ = ce.ErrorOutput.Sync() // ignore error } return @@ -253,7 +258,12 @@ func (ce *CheckedEntry) Write(fields ...Field) { err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields)) } if err != nil && ce.ErrorOutput != nil { - fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", ce.Time, err) + _, _ = fmt.Fprintf( + ce.ErrorOutput, + "%v write error: %v\n", + ce.Time, + err, + ) _ = ce.ErrorOutput.Sync() // ignore error } diff --git a/vendor/go.uber.org/zap/zapcore/lazy_with.go b/vendor/go.uber.org/zap/zapcore/lazy_with.go index 05288d6a88..500809de08 100644 --- a/vendor/go.uber.org/zap/zapcore/lazy_with.go +++ b/vendor/go.uber.org/zap/zapcore/lazy_with.go @@ -23,7 +23,8 @@ package zapcore import "sync" type lazyWithCore struct { - Core + core Core + originalCore Core sync.Once fields []Field } @@ -32,23 +33,45 @@ type lazyWithCore struct { // the logger is written to (or is further chained in a lon-lazy manner). func NewLazyWith(core Core, fields []Field) Core { return &lazyWithCore{ - Core: core, - fields: fields, + core: nil, // core is allocated once `initOnce` is called. + originalCore: core, + fields: fields, } } func (d *lazyWithCore) initOnce() { d.Once.Do(func() { - d.Core = d.Core.With(d.fields) + d.core = d.originalCore.With(d.fields) }) } func (d *lazyWithCore) With(fields []Field) Core { d.initOnce() - return d.Core.With(fields) + return d.core.With(fields) } func (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry { + // This is safe because `lazyWithCore` doesn't change the level. + // So we can delagate the level check, any not `initOnce` + // just for the check. + if !d.originalCore.Enabled(e.Level) { + return ce + } + d.initOnce() + return d.core.Check(e, ce) +} + +func (d *lazyWithCore) Enabled(level Level) bool { + // Like above, this is safe because `lazyWithCore` doesn't change the level. + return d.originalCore.Enabled(level) +} + +func (d *lazyWithCore) Write(e Entry, fields []Field) error { + d.initOnce() + return d.core.Write(e, fields) +} + +func (d *lazyWithCore) Sync() error { d.initOnce() - return d.Core.Check(e, ce) + return d.core.Sync() } diff --git a/vendor/go.uber.org/zap/zapcore/level.go b/vendor/go.uber.org/zap/zapcore/level.go index e01a241316..f3e166d67b 100644 --- a/vendor/go.uber.org/zap/zapcore/level.go +++ b/vendor/go.uber.org/zap/zapcore/level.go @@ -179,19 +179,19 @@ func (l *Level) UnmarshalText(text []byte) error { func (l *Level) unmarshalText(text []byte) bool { switch string(text) { - case "debug", "DEBUG": + case "debug": *l = DebugLevel - case "info", "INFO", "": // make the zero value useful + case "info", "": // make the zero value useful *l = InfoLevel - case "warn", "WARN": + case "warn", "warning": *l = WarnLevel - case "error", "ERROR": + case "error": *l = ErrorLevel - case "dpanic", "DPANIC": + case "dpanic": *l = DPanicLevel - case "panic", "PANIC": + case "panic": *l = PanicLevel - case "fatal", "FATAL": + case "fatal": *l = FatalLevel default: return false diff --git a/vendor/modules.txt b/vendor/modules.txt index 0158c9755b..3c9cb98543 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1747,7 +1747,7 @@ go.uber.org/mock/mockgen/model # go.uber.org/multierr v1.11.0 ## explicit; go 1.19 go.uber.org/multierr -# go.uber.org/zap v1.27.0 +# go.uber.org/zap v1.27.1 ## explicit; go 1.19 go.uber.org/zap go.uber.org/zap/buffer @@ -2664,7 +2664,7 @@ k8s.io/client-go/util/keyutil k8s.io/client-go/util/retry k8s.io/client-go/util/watchlist k8s.io/client-go/util/workqueue -# k8s.io/cluster-bootstrap v0.33.3 +# k8s.io/cluster-bootstrap v0.34.2 ## explicit; go 1.24.0 k8s.io/cluster-bootstrap/token/api k8s.io/cluster-bootstrap/token/util @@ -2746,7 +2746,7 @@ k8s.io/utils/trace # libvirt.org/go/libvirtxml v1.10002.0 ## explicit; go 1.11 libvirt.org/go/libvirtxml -# sigs.k8s.io/cluster-api v1.11.8 +# sigs.k8s.io/cluster-api v1.12.8 ## explicit; go 1.24.0 sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2 sigs.k8s.io/cluster-api/api/core/v1beta1 @@ -2796,7 +2796,7 @@ sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/optional ## explicit; go 1.24.0 sigs.k8s.io/cluster-api-provider-vsphere/apis/v1beta1 sigs.k8s.io/cluster-api-provider-vsphere/pkg/session -# sigs.k8s.io/controller-runtime v0.22.4 => sigs.k8s.io/controller-runtime v0.19.3 +# sigs.k8s.io/controller-runtime v0.22.5 => sigs.k8s.io/controller-runtime v0.19.3 ## explicit; go 1.22.0 sigs.k8s.io/controller-runtime sigs.k8s.io/controller-runtime/pkg/builder diff --git a/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2/kubeadm_types.go b/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2/kubeadm_types.go index e9f17e19a2..17e3bfc784 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2/kubeadm_types.go +++ b/vendor/sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2/kubeadm_types.go @@ -72,6 +72,23 @@ const ( KubeadmConfigDataSecretNotAvailableReason = clusterv1.NotAvailableReason ) +// EncryptionAlgorithmType can define an asymmetric encryption algorithm type. +// +kubebuilder:validation:Enum=ECDSA-P256;ECDSA-P384;RSA-2048;RSA-3072;RSA-4096 +type EncryptionAlgorithmType string + +const ( + // EncryptionAlgorithmECDSAP256 defines the ECDSA encryption algorithm type with curve P256. + EncryptionAlgorithmECDSAP256 EncryptionAlgorithmType = "ECDSA-P256" + // EncryptionAlgorithmECDSAP384 defines the ECDSA encryption algorithm type with curve P384. + EncryptionAlgorithmECDSAP384 EncryptionAlgorithmType = "ECDSA-P384" + // EncryptionAlgorithmRSA2048 defines the RSA encryption algorithm type with key size 2048 bits. + EncryptionAlgorithmRSA2048 EncryptionAlgorithmType = "RSA-2048" + // EncryptionAlgorithmRSA3072 defines the RSA encryption algorithm type with key size 3072 bits. + EncryptionAlgorithmRSA3072 EncryptionAlgorithmType = "RSA-3072" + // EncryptionAlgorithmRSA4096 defines the RSA encryption algorithm type with key size 4096 bits. + EncryptionAlgorithmRSA4096 EncryptionAlgorithmType = "RSA-4096" +) + // InitConfiguration contains a list of elements that is specific "kubeadm init"-only runtime // information. // +kubebuilder:validation:MinProperties=1 @@ -174,16 +191,7 @@ type ClusterConfiguration struct { CertificatesDir string `json:"certificatesDir,omitempty"` // imageRepository sets the container registry to pull images from. - // * If not set, the default registry of kubeadm will be used, i.e. - // * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0 - // * k8s.gcr.io (old registry): all older versions - // Please note that when imageRepository is not set we don't allow upgrades to - // versions >= v1.22.0 which use the old registry (k8s.gcr.io). Please use - // a newer patch version with the new registry instead (i.e. >= v1.22.17, - // >= v1.23.15, >= v1.24.9, >= v1.25.0). - // * If the version is a CI build (kubernetes version starts with `ci/` or `ci-cross/`) - // `gcr.io/k8s-staging-ci-images` will be used as a default for control plane components - // and for kube-proxy, while `registry.k8s.io` will be used for all the other images. + // If not set, the default registry of kubeadm will be used (registry.k8s.io). // +optional // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=512 @@ -208,6 +216,16 @@ type ClusterConfiguration struct { // +kubebuilder:validation:Minimum=1 // +kubebuilder:validation:Maximum=36500 CACertificateValidityPeriodDays int32 `json:"caCertificateValidityPeriodDays,omitempty"` + + // encryptionAlgorithm holds the type of asymmetric encryption algorithm used for keys and certificates. + // Can be one of "RSA-2048", "RSA-3072", "RSA-4096", "ECDSA-P256" or "ECDSA-P384". + // For Kubernetes 1.34 or above, "ECDSA-P384" is supported. + // If not specified, Cluster API will use RSA-2048 as default. + // When this field is modified every certificate generated afterward will use the new + // encryptionAlgorithm. Existing CA certificates and service account keys are not rotated. + // This field is only supported with Kubernetes v1.31 or above. + // +optional + EncryptionAlgorithm EncryptionAlgorithmType `json:"encryptionAlgorithm,omitempty"` } // IsDefined returns true if the ClusterConfiguration is defined. @@ -356,6 +374,11 @@ type APIEndpoint struct { BindPort int32 `json:"bindPort,omitempty"` } +// IsDefined returns true if the APIEndpoint is defined. +func (r *APIEndpoint) IsDefined() bool { + return r.AdvertiseAddress != "" || r.BindPort != 0 +} + // NodeRegistrationOptions holds fields that relate to registering a new control-plane or node to the cluster, either via "kubeadm init" or "kubeadm join". // Note: The NodeRegistrationOptions struct has to be kept in sync with the structs in MarshalJSON. // +kubebuilder:validation:MinProperties=1 diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/cluster_types.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/cluster_types.go index c6ff0853b1..5d6d69a9b3 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/cluster_types.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/cluster_types.go @@ -1092,12 +1092,12 @@ func (c *ClusterStatus) GetTypedPhase() ClusterPhase { type APIEndpoint struct { // host is the hostname on which the API server is serving. // TODO: Can't set MinLength=1 for now, because this struct is not always used in pointer fields so today we have cases where host is set to an empty string. - // +required + // +optional // +kubebuilder:validation:MaxLength=512 Host string `json:"host"` // port is the port on which the API server is serving. - // +required + // +optional Port int32 `json:"port"` } diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/conversion.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/conversion.go index 03d7a7a9ad..7d289566db 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/conversion.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/conversion.go @@ -73,6 +73,17 @@ func (src *Cluster) ConvertTo(dstRaw conversion.Hub) error { return err } + if ok { + dst.Spec.Topology.ControlPlane.HealthCheck.Checks.UnhealthyMachineConditions = restored.Spec.Topology.ControlPlane.HealthCheck.Checks.UnhealthyMachineConditions + for _, restoredMD := range restored.Spec.Topology.Workers.MachineDeployments { + for i, dstMD := range dst.Spec.Topology.Workers.MachineDeployments { + if restoredMD.Name == dstMD.Name { + dst.Spec.Topology.Workers.MachineDeployments[i].HealthCheck.Checks.UnhealthyMachineConditions = restoredMD.HealthCheck.Checks.UnhealthyMachineConditions + } + } + } + } + // Recover intent for bool values converted to *bool. clusterv1.Convert_bool_To_Pointer_bool(src.Spec.Paused, ok, restored.Spec.Paused, &dst.Spec.Paused) @@ -145,6 +156,17 @@ func (src *ClusterClass) ConvertTo(dstRaw conversion.Hub) error { return err } + if ok { + dst.Spec.ControlPlane.HealthCheck.Checks.UnhealthyMachineConditions = restored.Spec.ControlPlane.HealthCheck.Checks.UnhealthyMachineConditions + for _, restoredMD := range restored.Spec.Workers.MachineDeployments { + for i, dstMD := range dst.Spec.Workers.MachineDeployments { + if restoredMD.Class == dstMD.Class { + dst.Spec.Workers.MachineDeployments[i].HealthCheck.Checks.UnhealthyMachineConditions = restoredMD.HealthCheck.Checks.UnhealthyMachineConditions + } + } + } + } + // Recover intent for bool values converted to *bool. for i, patch := range dst.Spec.Patches { for j, definition := range patch.Definitions { @@ -248,6 +270,10 @@ func (src *ClusterClass) ConvertTo(dstRaw conversion.Hub) error { dst.Status.Variables[i] = variable } + dst.Spec.KubernetesVersions = restored.Spec.KubernetesVersions + + dst.Spec.Upgrade.External.GenerateUpgradePlanExtension = restored.Spec.Upgrade.External.GenerateUpgradePlanExtension + return nil } @@ -394,6 +420,11 @@ func (src *Machine) ConvertTo(dstRaw conversion.Hub) error { // Recover other values. if ok { dst.Spec.MinReadySeconds = restored.Spec.MinReadySeconds + dst.Spec.Taints = restored.Spec.Taints + // Restore the phase, this also means that any client using v1beta1 during a round-trip + // won't be able to write the Phase field. But that's okay as the only client writing the Phase + // field should be the Machine controller. + dst.Status.Phase = restored.Status.Phase } return nil @@ -432,6 +463,17 @@ func (src *MachineSet) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.Template.Spec.MinReadySeconds = &src.Spec.MinReadySeconds } + restored := &clusterv1.MachineSet{} + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { + return err + } + + // Recover other values + if ok { + dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints + } + return nil } @@ -449,7 +491,8 @@ func (dst *MachineSet) ConvertFrom(srcRaw conversion.Hub) error { dst.Spec.MinReadySeconds = ptr.Deref(src.Spec.Template.Spec.MinReadySeconds, 0) dropEmptyStringsMachineSpec(&dst.Spec.Template.Spec) - return nil + + return utilconversion.MarshalData(src, dst) } func (src *MachineDeployment) ConvertTo(dstRaw conversion.Hub) error { @@ -474,6 +517,11 @@ func (src *MachineDeployment) ConvertTo(dstRaw conversion.Hub) error { // Recover intent for bool values converted to *bool. clusterv1.Convert_bool_To_Pointer_bool(src.Spec.Paused, ok, restored.Spec.Paused, &dst.Spec.Paused) + // Recover other values + if ok { + dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints + } + return nil } @@ -509,6 +557,8 @@ func (src *MachineHealthCheck) ConvertTo(dstRaw conversion.Hub) error { return err } + dst.Spec.Checks.UnhealthyMachineConditions = restored.Spec.Checks.UnhealthyMachineConditions + clusterv1.Convert_int32_To_Pointer_int32(src.Status.ExpectedMachines, ok, restored.Status.ExpectedMachines, &dst.Status.ExpectedMachines) clusterv1.Convert_int32_To_Pointer_int32(src.Status.CurrentHealthy, ok, restored.Status.CurrentHealthy, &dst.Status.CurrentHealthy) clusterv1.Convert_int32_To_Pointer_int32(src.Status.RemediationsAllowed, ok, restored.Status.RemediationsAllowed, &dst.Status.RemediationsAllowed) @@ -558,6 +608,11 @@ func (src *MachinePool) ConvertTo(dstRaw conversion.Hub) error { dst.Status.Initialization = initialization } + // Recover other values + if ok { + dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints + } + return nil } @@ -1637,6 +1692,13 @@ func Convert_v1beta2_MachineStatus_To_v1beta1_MachineStatus(in *clusterv1.Machin if err := autoConvert_v1beta2_MachineStatus_To_v1beta1_MachineStatus(in, out, s); err != nil { return err } + + // Convert v1beta2 Updating phase to v1beta1 Running as Updating did not exist in v1beta1. + // We don't have to support a round-trip as only the core CAPI controller should write the Phase field. + if out.Phase == "Updating" { + out.Phase = "Running" + } + if !reflect.DeepEqual(in.LastUpdated, metav1.Time{}) { out.LastUpdated = ptr.To(in.LastUpdated) } @@ -2285,8 +2347,6 @@ func convertToObjectReference(ref clusterv1.ContractVersionedObjectReference, na } func Convert_v1beta1_JSONSchemaProps_To_v1beta2_JSONSchemaProps(in *JSONSchemaProps, out *clusterv1.JSONSchemaProps, s apimachineryconversion.Scope) error { - // This conversion func is also required due to a bug in conversion gen that does not recognize the changes for converting bool to *bool. - // By implementing this func, autoConvert_v1beta1_JSONSchemaProps_To_v1beta2_JSONSchemaProps is generated properly. if err := autoConvert_v1beta1_JSONSchemaProps_To_v1beta2_JSONSchemaProps(in, out, s); err != nil { return err } diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/machine_types.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/machine_types.go index 9665953dd4..5a37f75427 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/machine_types.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/machine_types.go @@ -66,10 +66,10 @@ const ( // * KCP adds its own pre-terminate hook on all Machines it controls. This is done to ensure it can later remove // the etcd member right before Machine termination (i.e. before InfraMachine deletion). // * Starting with Kubernetes v1.31 the KCP pre-terminate hook will wait for all other pre-terminate hooks to finish to - // ensure it runs last (thus ensuring that kubelet is still working while other pre-terminate hooks run). This is only done - // for v1.31 or above because the kubeadm ControlPlaneKubeletLocalMode was introduced with kubeadm 1.31. This feature configures - // the kubelet to communicate with the local apiserver. Only because of that the kubelet immediately starts failing after the etcd - // member is removed. We need the ControlPlaneKubeletLocalMode feature with 1.31 to adhere to the kubelet skew policy. + // ensure it runs last (thus ensuring that kubelet is still working while other pre-terminate hooks run). This is done + // for v1.31 or above because the kubeadm ControlPlaneKubeletLocalMode was introduced with kubeadm 1.31 (graduated to GA in 1.36). + // This feature configures the kubelet to communicate with the local apiserver. Only because of that the kubelet immediately + // starts failing after the etcd member is removed. We need the ControlPlaneKubeletLocalMode feature with 1.31+ to adhere to the kubelet skew policy. PreTerminateDeleteHookAnnotationPrefix = "pre-terminate.delete.hook.machine.cluster.x-k8s.io" // MachineCertificatesExpiryDateAnnotation annotation specifies the expiry date of the machine certificates in RFC3339 format. diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.conversion.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.conversion.go index eb54f254dd..c0514e67d2 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.conversion.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.conversion.go @@ -1207,6 +1207,8 @@ func autoConvert_v1beta2_ClusterClassSpec_To_v1beta1_ClusterClassSpec(in *v1beta } else { out.Patches = nil } + // WARNING: in.Upgrade requires manual conversion: does not exist in peer-type + // WARNING: in.KubernetesVersions requires manual conversion: does not exist in peer-type return nil } @@ -3165,6 +3167,7 @@ func autoConvert_v1beta2_MachineSpec_To_v1beta1_MachineSpec(in *v1beta2.MachineS // WARNING: in.MinReadySeconds requires manual conversion: does not exist in peer-type out.ReadinessGates = *(*[]MachineReadinessGate)(unsafe.Pointer(&in.ReadinessGates)) // WARNING: in.Deletion requires manual conversion: does not exist in peer-type + // WARNING: in.Taints requires manual conversion: does not exist in peer-type return nil } diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.openapi.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.openapi.go index 13a78b2371..723cf5a90c 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.openapi.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta1/zz_generated.openapi.go @@ -154,7 +154,6 @@ func schema_cluster_api_api_core_v1beta1_APIEndpoint(ref common.ReferenceCallbac }, }, }, - Required: []string{"host", "port"}, }, }, } diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/cluster_types.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/cluster_types.go index af09698531..9666455e90 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/cluster_types.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/cluster_types.go @@ -80,17 +80,27 @@ const ( // failing due to an error. ClusterTopologyReconciledFailedReason = "ReconcileFailed" + // ClusterTopologyReconciledClusterCreatingReason documents reconciliation of a Cluster topology + // not yet created because the BeforeClusterCreate hook is blocking. + ClusterTopologyReconciledClusterCreatingReason = "ClusterCreating" + // ClusterTopologyReconciledControlPlaneUpgradePendingReason documents reconciliation of a Cluster topology // not yet completed because Control Plane is not yet updated to match the desired topology spec. + // + // Deprecated: please use ClusterUpgrading instead. ClusterTopologyReconciledControlPlaneUpgradePendingReason = "ControlPlaneUpgradePending" // ClusterTopologyReconciledMachineDeploymentsCreatePendingReason documents reconciliation of a Cluster topology // not yet completed because at least one of the MachineDeployments is yet to be created. // This generally happens because new MachineDeployment creations are held off while the ControlPlane is not stable. + // + // Deprecated: please use ClusterUpgrading instead. ClusterTopologyReconciledMachineDeploymentsCreatePendingReason = "MachineDeploymentsCreatePending" // ClusterTopologyReconciledMachineDeploymentsUpgradePendingReason documents reconciliation of a Cluster topology // not yet completed because at least one of the MachineDeployments is not yet updated to match the desired topology spec. + // + // Deprecated: please use ClusterUpgrading instead. ClusterTopologyReconciledMachineDeploymentsUpgradePendingReason = "MachineDeploymentsUpgradePending" // ClusterTopologyReconciledMachineDeploymentsUpgradeDeferredReason documents reconciliation of a Cluster topology @@ -99,11 +109,15 @@ const ( // ClusterTopologyReconciledMachinePoolsUpgradePendingReason documents reconciliation of a Cluster topology // not yet completed because at least one of the MachinePools is not yet updated to match the desired topology spec. + // + // Deprecated: please use ClusterUpgrading instead. ClusterTopologyReconciledMachinePoolsUpgradePendingReason = "MachinePoolsUpgradePending" // ClusterTopologyReconciledMachinePoolsCreatePendingReason documents reconciliation of a Cluster topology // not yet completed because at least one of the MachinePools is yet to be created. // This generally happens because new MachinePool creations are held off while the ControlPlane is not stable. + // + // Deprecated: please use ClusterUpgrading instead. ClusterTopologyReconciledMachinePoolsCreatePendingReason = "MachinePoolsCreatePending" // ClusterTopologyReconciledMachinePoolsUpgradeDeferredReason documents reconciliation of a Cluster topology @@ -112,8 +126,13 @@ const ( // ClusterTopologyReconciledHookBlockingReason documents reconciliation of a Cluster topology // not yet completed because at least one of the lifecycle hooks is blocking. + // + // Deprecated: please use ClusterUpgrading instead. ClusterTopologyReconciledHookBlockingReason = "LifecycleHookBlocking" + // ClusterTopologyReconciledClusterUpgradingReason documents reconciliation of a Cluster topology + // not yet completed because a cluster upgrade is still in progress. + ClusterTopologyReconciledClusterUpgradingReason = "ClusterUpgrading" // ClusterTopologyReconciledClusterClassNotReconciledReason documents reconciliation of a Cluster topology not // yet completed because the ClusterClass has not reconciled yet. If this condition persists there may be an issue // with the ClusterClass surfaced in the ClusterClass status or controller logs. @@ -725,6 +744,16 @@ type ControlPlaneTopologyHealthCheckChecks struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=100 UnhealthyNodeConditions []UnhealthyNodeCondition `json:"unhealthyNodeConditions,omitempty"` + + // unhealthyMachineConditions contains a list of the machine conditions that determine + // whether a machine is considered unhealthy. The conditions are combined in a + // logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + // + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + UnhealthyMachineConditions []UnhealthyMachineCondition `json:"unhealthyMachineConditions,omitempty"` } // ControlPlaneTopologyHealthCheckRemediation configures if and how remediations are triggered if a control plane Machine is unhealthy. @@ -975,6 +1004,16 @@ type MachineDeploymentTopologyHealthCheckChecks struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=100 UnhealthyNodeConditions []UnhealthyNodeCondition `json:"unhealthyNodeConditions,omitempty"` + + // unhealthyMachineConditions contains a list of the machine conditions that determine + // whether a machine is considered unhealthy. The conditions are combined in a + // logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + // + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + UnhealthyMachineConditions []UnhealthyMachineCondition `json:"unhealthyMachineConditions,omitempty"` } // MachineDeploymentTopologyHealthCheckRemediation configures if and how remediations are triggered if a MachineDeployment Machine is unhealthy. diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/clusterclass_types.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/clusterclass_types.go index 80d78f3585..12e8cc19c5 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/clusterclass_types.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/clusterclass_types.go @@ -135,6 +135,22 @@ type ClusterClassSpec struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=1000 Patches []ClusterClassPatch `json:"patches,omitempty"` + + // upgrade defines the upgrade configuration for clusters using this ClusterClass. + // +optional + Upgrade ClusterClassUpgrade `json:"upgrade,omitempty,omitzero"` + + // kubernetesVersions is the list of Kubernetes versions that can be + // used for clusters using this ClusterClass. + // The list of version must be ordered from the older to the newer version, and there should be + // at least one version for every minor in between the first and the last version. + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=256 + KubernetesVersions []string `json:"kubernetesVersions,omitempty"` } // InfrastructureClass defines the class for the infrastructure cluster. @@ -265,6 +281,16 @@ type ControlPlaneClassHealthCheckChecks struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=100 UnhealthyNodeConditions []UnhealthyNodeCondition `json:"unhealthyNodeConditions,omitempty"` + + // unhealthyMachineConditions contains a list of the machine conditions that determine + // whether a machine is considered unhealthy. The conditions are combined in a + // logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + // + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + UnhealthyMachineConditions []UnhealthyMachineCondition `json:"unhealthyMachineConditions,omitempty"` } // ControlPlaneClassHealthCheckRemediation configures if and how remediations are triggered if a control plane Machine is unhealthy. @@ -526,6 +552,16 @@ type MachineDeploymentClassHealthCheckChecks struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=100 UnhealthyNodeConditions []UnhealthyNodeCondition `json:"unhealthyNodeConditions,omitempty"` + + // unhealthyMachineConditions contains a list of the machine conditions that determine + // whether a machine is considered unhealthy. The conditions are combined in a + // logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + // + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + UnhealthyMachineConditions []UnhealthyMachineCondition `json:"unhealthyMachineConditions,omitempty"` } // MachineDeploymentClassHealthCheckRemediation configures if and how remediations are triggered if a MachineDeployment Machine is unhealthy. @@ -1240,6 +1276,24 @@ type ClusterClassPatch struct { External *ExternalPatchDefinition `json:"external,omitempty"` } +// ClusterClassUpgrade defines the upgrade configuration for clusters using the ClusterClass. +// +kubebuilder:validation:MinProperties=1 +type ClusterClassUpgrade struct { + // external defines external runtime extensions for upgrade operations. + // +optional + External ClusterClassUpgradeExternal `json:"external,omitempty,omitzero"` +} + +// ClusterClassUpgradeExternal defines external runtime extensions for upgrade operations. +// +kubebuilder:validation:MinProperties=1 +type ClusterClassUpgradeExternal struct { + // generateUpgradePlanExtension references an extension which is called to generate upgrade plan. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + GenerateUpgradePlanExtension string `json:"generateUpgradePlanExtension,omitempty"` +} + // PatchDefinition defines a patch which is applied to customize the referenced templates. type PatchDefinition struct { // selector defines on which templates the patch should be applied. diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/common_types.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/common_types.go index c0d39fd02c..13b591c182 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/common_types.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/common_types.go @@ -36,6 +36,10 @@ const ( // to track the name of the MachineDeployment topology it represents. ClusterTopologyMachineDeploymentNameLabel = "topology.cluster.x-k8s.io/deployment-name" + // ClusterTopologyUpgradeStepAnnotation tracks the version of the current upgrade step. + // It is only set when an upgrade is in progress, and it contains the control plane version computed by topology controller. + ClusterTopologyUpgradeStepAnnotation = "topology.internal.cluster.x-k8s.io/upgrade-step" + // ClusterTopologyHoldUpgradeSequenceAnnotation can be used to hold the entire MachineDeployment upgrade sequence. // If the annotation is set on a MachineDeployment topology in Cluster.spec.topology.workers, the Kubernetes upgrade // for this MachineDeployment topology and all subsequent ones is deferred. @@ -95,6 +99,9 @@ const ( // AnnotationsFromMachineAnnotation is the annotation set on nodes to track the annotations that originated from machines. AnnotationsFromMachineAnnotation = "cluster.x-k8s.io/annotations-from-machine" + // TaintsFromMachineAnnotation is the annotation set on nodes to track the taints that originated from machines. + TaintsFromMachineAnnotation = "cluster.x-k8s.io/taints-from-machine" + // OwnerNameAnnotation is the annotation set on nodes identifying the owner name. OwnerNameAnnotation = "cluster.x-k8s.io/owner-name" @@ -401,3 +408,58 @@ func (r *ContractVersionedObjectReference) GroupKind() schema.GroupKind { Kind: r.Kind, } } + +// MachineTaint defines a taint equivalent to corev1.Taint, but additionally having a propagation field. +type MachineTaint struct { + // key is the taint key to be applied to a node. + // Must be a valid qualified name of maximum size 63 characters + // with an optional subdomain prefix of maximum size 253 characters, + // separated by a `/`. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=317 + // +kubebuilder:validation:Pattern=^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/)?([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$ + // +kubebuilder:validation:XValidation:rule="self.contains('/') ? ( self.split('/') [0].size() <= 253 && self.split('/') [1].size() <= 63 && self.split('/').size() == 2 ) : self.size() <= 63",message="key must be a valid qualified name of max size 63 characters with an optional subdomain prefix of max size 253 characters" + Key string `json:"key,omitempty"` + + // value is the taint value corresponding to the taint key. + // It must be a valid label value of maximum size 63 characters. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=63 + // +kubebuilder:validation:Pattern=^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ + Value string `json:"value,omitempty"` + + // effect is the effect for the taint. Valid values are NoSchedule, PreferNoSchedule and NoExecute. + // +required + // +kubebuilder:validation:Enum=NoSchedule;PreferNoSchedule;NoExecute + Effect corev1.TaintEffect `json:"effect,omitempty"` + + // propagation defines how this taint should be propagated to nodes. + // Valid values are 'Always' and 'OnInitialization'. + // Always: The taint will be continuously reconciled. If it is not set for a node, it will be added during reconciliation. + // OnInitialization: The taint will be added during node initialization. If it gets removed from the node later on it will not get added again. + // +required + Propagation MachineTaintPropagation `json:"propagation,omitempty"` +} + +// MachineTaintPropagation defines when a taint should be propagated to nodes. +// +kubebuilder:validation:Enum=Always;OnInitialization +type MachineTaintPropagation string + +const ( + // MachineTaintPropagationAlways means the taint should be continuously reconciled and kept on the node. + // - If an Always taint is added to the Machine, the taint will be added to the node. + // - If an Always taint is removed from the Machine, the taint will be removed from the node. + // - If an OnInitialization taint is changed to Always, the Machine controller will ensure the taint is set on the node. + // - If an Always taint is removed from the node, it will be re-added during reconciliation. + MachineTaintPropagationAlways MachineTaintPropagation = "Always" + + // MachineTaintPropagationOnInitialization means the taint should be set once during initialization and then + // left alone. + // - If an OnInitialization taint is added to the Machine, the taint will only be added to the node on initialization. + // - If an OnInitialization taint is removed from the Machine nothing will be changed on the node. + // - If an Always taint is changed to OnInitialization, the taint will only be added to the node on initialization. + // - If an OnInitialization taint is removed from the node, it will not be re-added during reconciliation. + MachineTaintPropagationOnInitialization MachineTaintPropagation = "OnInitialization" +) diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_phase_types.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_phase_types.go index 1ca9551564..b849ea61d6 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_phase_types.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_phase_types.go @@ -45,6 +45,10 @@ const ( // become a Kubernetes Node in a Ready state. MachinePhaseRunning = MachinePhase("Running") + // MachinePhaseUpdating is the Machine state when the Machine + // is updating. + MachinePhaseUpdating = MachinePhase("Updating") + // MachinePhaseDeleting is the Machine state when a delete // request has been sent to the API Server, // but its infrastructure has not yet been fully deleted. diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_types.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_types.go index a60f736ba2..16f4bf07a7 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_types.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machine_types.go @@ -66,10 +66,10 @@ const ( // * KCP adds its own pre-terminate hook on all Machines it controls. This is done to ensure it can later remove // the etcd member right before Machine termination (i.e. before InfraMachine deletion). // * Starting with Kubernetes v1.31 the KCP pre-terminate hook will wait for all other pre-terminate hooks to finish to - // ensure it runs last (thus ensuring that kubelet is still working while other pre-terminate hooks run). This is only done - // for v1.31 or above because the kubeadm ControlPlaneKubeletLocalMode was introduced with kubeadm 1.31. This feature configures - // the kubelet to communicate with the local apiserver. Only because of that the kubelet immediately starts failing after the etcd - // member is removed. We need the ControlPlaneKubeletLocalMode feature with 1.31 to adhere to the kubelet skew policy. + // ensure it runs last (thus ensuring that kubelet is still working while other pre-terminate hooks run). This is done + // for v1.31 or above because the kubeadm ControlPlaneKubeletLocalMode was introduced with kubeadm 1.31 (graduated to GA in 1.36). + // This feature configures the kubelet to communicate with the local apiserver. Only because of that the kubelet immediately + // starts failing after the etcd member is removed. We need the ControlPlaneKubeletLocalMode feature with 1.31+ to adhere to the kubelet skew policy. PreTerminateDeleteHookAnnotationPrefix = "pre-terminate.delete.hook.machine.cluster.x-k8s.io" // MachineCertificatesExpiryDateAnnotation annotation specifies the expiry date of the machine certificates in RFC3339 format. @@ -87,6 +87,17 @@ const ( // ManagedNodeAnnotationDomain is one of the CAPI managed Node annotation domains. ManagedNodeAnnotationDomain = "node.cluster.x-k8s.io" + + // PendingAcknowledgeMoveAnnotation is an internal annotation added by the MS controller to a machine when being + // moved from the oldMS to the newMS. The annotation is removed as soon as the MS controller get the acknowledgment about the + // replica being accounted from the corresponding MD. + // Note: The annotation is added when reconciling the oldMS, and it is removed when reconciling the newMS. + // Note: This annotation is used in pair with AcknowledgedMoveAnnotation on MachineSets. + PendingAcknowledgeMoveAnnotation = "in-place-updates.internal.cluster.x-k8s.io/pending-acknowledge-move" + + // UpdateInProgressAnnotation is an internal annotation added to machines by the controller owning the Machine when in-place update + // is started, e.g. by the MachineSet controller; the annotation will be removed by the Machine controller when in-place update is completed. + UpdateInProgressAnnotation = "in-place-updates.internal.cluster.x-k8s.io/update-in-progress" ) // Machine's Available condition and corresponding reasons. @@ -109,7 +120,7 @@ const ( // Machine's Ready condition and corresponding reasons. const ( // MachineReadyCondition is true if the Machine's deletionTimestamp is not set, Machine's BootstrapConfigReady, InfrastructureReady, - // NodeHealthy and HealthCheckSucceeded (if present) conditions are true; if other conditions are defined in spec.readinessGates, + // NodeHealthy and HealthCheckSucceeded (if present) conditions are true, Updating condition is false; if other conditions are defined in spec.readinessGates, // these conditions must be true as well. // Note: // - When summarizing the Deleting condition: @@ -151,6 +162,28 @@ const ( // MachineNotUpToDateReason surface when a Machine spec does not match the spec of the Machine's owner resource, e.g. KubeadmControlPlane or MachineDeployment. MachineNotUpToDateReason = "NotUpToDate" + + // MachineUpToDateUpdatingReason surface when a Machine spec matches the spec of the Machine's owner resource, + // but the Machine is still updating in-place. + MachineUpToDateUpdatingReason = "Updating" +) + +// Machine's Updating condition and corresponding reasons. +// Note: Updating condition is set by the Machine controller during in-place updates. +const ( + // MachineUpdatingCondition is true while an in-place update is in progress on the Machine. + // The condition is owned by the Machine controller and is used to track the progress of in-place updates. + // This condition is considered when computing the UpToDate condition. + MachineUpdatingCondition = "Updating" + + // MachineNotUpdatingReason surfaces when the Machine is not performing an in-place update. + MachineNotUpdatingReason = "NotUpdating" + + // MachineInPlaceUpdatingReason surfaces when the Machine is waiting for in-place update to complete. + MachineInPlaceUpdatingReason = "InPlaceUpdating" + + // MachineInPlaceUpdateFailedReason surfaces when the in-place update has failed. + MachineInPlaceUpdateFailedReason = "InPlaceUpdateFailed" ) // Machine's BootstrapConfigReady condition and corresponding reasons. @@ -276,6 +309,10 @@ const ( // defined by a MachineHealthCheck object. MachineHealthCheckUnhealthyNodeReason = "UnhealthyNode" + // MachineHealthCheckUnhealthyMachineReason surfaces when the machine does not pass the health checks + // defined by a MachineHealthCheck object. + MachineHealthCheckUnhealthyMachineReason = "UnhealthyMachine" + // MachineHealthCheckNodeStartupTimeoutReason surfaces when the node hosted on the machine does not appear within // the timeout defined by a MachineHealthCheck object. MachineHealthCheckNodeStartupTimeoutReason = "NodeStartupTimeout" @@ -451,6 +488,23 @@ type MachineSpec struct { // deletion contains configuration options for Machine deletion. // +optional Deletion MachineDeletionSpec `json:"deletion,omitempty,omitzero"` + + // taints are the node taints that Cluster API will manage. + // This list is not necessarily complete: other Kubernetes components may add or remove other taints from nodes, + // e.g. the node controller might add the node.kubernetes.io/not-ready taint. + // Only those taints defined in this list will be added or removed by core Cluster API controllers. + // + // There can be at most 64 taints. + // A pod would have to tolerate all existing taints to run on the corresponding node. + // + // NOTE: This list is implemented as a "map" type, meaning that individual elements can be managed by different owners. + // +optional + // +listType=map + // +listMapKey=key + // +listMapKey=effect + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=64 + Taints []MachineTaint `json:"taints,omitempty"` } // MachineDeletionSpec contains configuration options for Machine deletion. @@ -502,7 +556,7 @@ type MachineReadinessGate struct { type MachineStatus struct { // conditions represents the observations of a Machine's current state. // Known condition types are Available, Ready, UpToDate, BootstrapConfigReady, InfrastructureReady, NodeReady, - // NodeHealthy, Deleting, Paused. + // NodeHealthy, Updating, Deleting, Paused. // If a MachineHealthCheck is targeting this machine, also HealthCheckSucceeded, OwnerRemediated conditions are added. // Additionally control plane Machines controlled by KubeadmControlPlane will have following additional conditions: // APIServerPodHealthy, ControllerManagerPodHealthy, SchedulerPodHealthy, EtcdPodHealthy, EtcdMemberHealthy. @@ -537,7 +591,7 @@ type MachineStatus struct { // phase represents the current phase of machine actuation. // +optional - // +kubebuilder:validation:Enum=Pending;Provisioning;Provisioned;Running;Deleting;Deleted;Failed;Unknown + // +kubebuilder:validation:Enum=Pending;Provisioning;Provisioned;Running;Updating;Deleting;Deleted;Failed;Unknown Phase string `json:"phase,omitempty"` // certificatesExpiryDate is the expiry date of the machine certificates. @@ -695,6 +749,7 @@ func (m *MachineStatus) GetTypedPhase() MachinePhase { MachinePhaseProvisioning, MachinePhaseProvisioned, MachinePhaseRunning, + MachinePhaseUpdating, MachinePhaseDeleting, MachinePhaseDeleted, MachinePhaseFailed: diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machinehealthcheck_types.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machinehealthcheck_types.go index 9a7e31cae4..9a1f66bc8b 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machinehealthcheck_types.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machinehealthcheck_types.go @@ -111,6 +111,16 @@ type MachineHealthCheckChecks struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=100 UnhealthyNodeConditions []UnhealthyNodeCondition `json:"unhealthyNodeConditions,omitempty"` + + // unhealthyMachineConditions contains a list of the machine conditions that determine + // whether a machine is considered unhealthy. The conditions are combined in a + // logical OR, i.e. if any of the conditions is met, the machine is unhealthy. + // + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + UnhealthyMachineConditions []UnhealthyMachineCondition `json:"unhealthyMachineConditions,omitempty"` } // MachineHealthCheckRemediation configures if and how remediations are triggered if a Machine is unhealthy. @@ -227,7 +237,33 @@ type UnhealthyNodeCondition struct { // timeoutSeconds is the duration that a node must be in a given status for, // after which the node is considered unhealthy. - // For example, with a value of "1h", the node must match the status + // For example, with a value of "3600", the node must match the status + // for at least 1 hour before being considered unhealthy. + // +required + // +kubebuilder:validation:Minimum=0 + TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty"` +} + +// UnhealthyMachineCondition represents a Machine condition type and value with a timeout +// specified as a duration. When the named condition has been in the given +// status for at least the timeout value, a machine is considered unhealthy. +type UnhealthyMachineCondition struct { + // type of Machine condition + // +kubebuilder:validation:Pattern=`^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$` + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=316 + // +kubebuilder:validation:XValidation:rule="!(self in ['Ready','Available','HealthCheckSucceeded','OwnerRemediated','ExternallyRemediated'])",message="type must not be one of: Ready, Available, HealthCheckSucceeded, OwnerRemediated, ExternallyRemediated" + // +required + Type string `json:"type,omitempty"` + + // status of the condition, one of True, False, Unknown. + // +required + // +kubebuilder:validation:Enum=True;False;Unknown + Status metav1.ConditionStatus `json:"status,omitempty"` + + // timeoutSeconds is the duration that a machine must be in a given status for, + // after which the machine is considered unhealthy. + // For example, with a value of "3600", the machine must match the status // for at least 1 hour before being considered unhealthy. // +required // +kubebuilder:validation:Minimum=0 diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machineset_types.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machineset_types.go index 8a5a92db7d..80cb19d362 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machineset_types.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/machineset_types.go @@ -33,6 +33,29 @@ const ( // MachineSetFinalizer is the finalizer used by the MachineSet controller to // ensure ordered cleanup of corresponding Machines when a Machineset is being deleted. MachineSetFinalizer = "cluster.x-k8s.io/machineset" + + // MachineSetMoveMachinesToMachineSetAnnotation is an internal annotation added by the MD controller to the oldMS + // when it should scale down by moving machines that can be updated in-place to the newMS instead of deleting them. + // The annotation value is the newMS name. + // Note: This annotation is used in pair with MachineSetReceiveMachinesFromMachineSetsAnnotation to perform a two-ways check before moving a machine from oldMS to newMS: + // + // "oldMS must have: move to newMS" and "newMS must have: receive replicas from oldMS" + MachineSetMoveMachinesToMachineSetAnnotation = "in-place-updates.internal.cluster.x-k8s.io/move-machines-to-machineset" + + // MachineSetReceiveMachinesFromMachineSetsAnnotation is an internal annotation added by the MD controller to the newMS + // when it should receive replicas from oldMSs as a first step of an in-place update operation + // The annotation value is a comma separated list of oldMSs. + // Note: This annotation is used in pair with MachineSetMoveMachinesToMachineSetAnnotation to perform a two-ways check before moving a machine from oldMS to newMS: + // + // "oldMS must have: move to newMS" and "newMS must have: receive replicas from oldMS" + MachineSetReceiveMachinesFromMachineSetsAnnotation = "in-place-updates.internal.cluster.x-k8s.io/receive-machines-from-machinesets" + + // AcknowledgedMoveAnnotation is an internal annotation with a list of machines added by the MD controller + // to a MachineSet when it acknowledges a machine pending acknowledge after being moved from an oldMS. + // The annotation value is a comma separated list of Machines already acknowledged; a machine is dropped + // from this annotation as soon as pending-acknowledge-move is removed from the machine; the annotation is dropped when empty. + // Note: This annotation is used in pair with PendingAcknowledgeMoveAnnotation on Machines. + AcknowledgedMoveAnnotation = "in-place-updates.internal.cluster.x-k8s.io/acknowledged-move" ) // MachineSetSpec defines the desired state of MachineSet. diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/v1beta1_condition_consts.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/v1beta1_condition_consts.go index aef565c0aa..b619c6e0d7 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/v1beta1_condition_consts.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/v1beta1_condition_consts.go @@ -157,6 +157,11 @@ const ( // UnhealthyNodeConditionV1Beta1Reason is the reason used when a machine's node has one of the MachineHealthCheck's unhealthy conditions. UnhealthyNodeConditionV1Beta1Reason = "UnhealthyNode" + + // UnhealthyMachineConditionV1Beta1Reason is the reason used when a machine has one of the MachineHealthCheck's unhealthy conditions. + // When both machine and node issues are detected, this reason takes precedence over node-related reasons + // (NodeNotFoundV1Beta1Reason, NodeStartupTimeoutV1Beta1Reason, UnhealthyNodeConditionV1Beta1Reason). + UnhealthyMachineConditionV1Beta1Reason = "UnhealthyMachine" ) const ( @@ -295,17 +300,27 @@ const ( // failing due to an error. TopologyReconcileFailedV1Beta1Reason = "TopologyReconcileFailed" + // TopologyReconciledClusterCreatingV1Beta1Reason documents reconciliation of a Cluster topology + // not yet created because the BeforeClusterCreate hook is blocking. + TopologyReconciledClusterCreatingV1Beta1Reason = "ClusterCreating" + // TopologyReconciledControlPlaneUpgradePendingV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology // not yet completed because Control Plane is not yet updated to match the desired topology spec. + // + // Deprecated: please use ClusterUpgrading instead. TopologyReconciledControlPlaneUpgradePendingV1Beta1Reason = "ControlPlaneUpgradePending" // TopologyReconciledMachineDeploymentsCreatePendingV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology // not yet completed because at least one of the MachineDeployments is yet to be created. // This generally happens because new MachineDeployment creations are held off while the ControlPlane is not stable. + // + // Deprecated: please use ClusterUpgrading instead. TopologyReconciledMachineDeploymentsCreatePendingV1Beta1Reason = "MachineDeploymentsCreatePending" // TopologyReconciledMachineDeploymentsUpgradePendingV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology // not yet completed because at least one of the MachineDeployments is not yet updated to match the desired topology spec. + // + // Deprecated: please use ClusterUpgrading instead. TopologyReconciledMachineDeploymentsUpgradePendingV1Beta1Reason = "MachineDeploymentsUpgradePending" // TopologyReconciledMachineDeploymentsUpgradeDeferredV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology @@ -314,11 +329,15 @@ const ( // TopologyReconciledMachinePoolsUpgradePendingV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology // not yet completed because at least one of the MachinePools is not yet updated to match the desired topology spec. + // + // Deprecated: please use ClusterUpgrading instead. TopologyReconciledMachinePoolsUpgradePendingV1Beta1Reason = "MachinePoolsUpgradePending" // TopologyReconciledMachinePoolsCreatePendingV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology // not yet completed because at least one of the MachinePools is yet to be created. // This generally happens because new MachinePool creations are held off while the ControlPlane is not stable. + // + // Deprecated: please use ClusterUpgrading instead. TopologyReconciledMachinePoolsCreatePendingV1Beta1Reason = "MachinePoolsCreatePending" // TopologyReconciledMachinePoolsUpgradeDeferredV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology @@ -327,8 +346,14 @@ const ( // TopologyReconciledHookBlockingV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology // not yet completed because at least one of the lifecycle hooks is blocking. + // + // Deprecated: please use ClusterUpgrading instead. TopologyReconciledHookBlockingV1Beta1Reason = "LifecycleHookBlocking" + // TopologyReconciledClusterUpgradingV1Beta1Reason documents reconciliation of a Cluster topology + // not yet completed because a cluster upgrade is still in progress. + TopologyReconciledClusterUpgradingV1Beta1Reason = "ClusterUpgrading" + // TopologyReconciledClusterClassNotReconciledV1Beta1Reason (Severity=Info) documents reconciliation of a Cluster topology not // yet completed because the ClusterClass has not reconciled yet. If this condition persists there may be an issue // with the ClusterClass surfaced in the ClusterClass status or controller logs. diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.deepcopy.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.deepcopy.go index 49d1f66552..5adb8a56c4 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.deepcopy.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.deepcopy.go @@ -253,6 +253,12 @@ func (in *ClusterClassSpec) DeepCopyInto(out *ClusterClassSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + out.Upgrade = in.Upgrade + if in.KubernetesVersions != nil { + in, out := &in.KubernetesVersions, &out.KubernetesVersions + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterClassSpec. @@ -363,6 +369,37 @@ func (in *ClusterClassTemplateReference) DeepCopy() *ClusterClassTemplateReferen return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterClassUpgrade) DeepCopyInto(out *ClusterClassUpgrade) { + *out = *in + out.External = in.External +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterClassUpgrade. +func (in *ClusterClassUpgrade) DeepCopy() *ClusterClassUpgrade { + if in == nil { + return nil + } + out := new(ClusterClassUpgrade) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterClassUpgradeExternal) DeepCopyInto(out *ClusterClassUpgradeExternal) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterClassUpgradeExternal. +func (in *ClusterClassUpgradeExternal) DeepCopy() *ClusterClassUpgradeExternal { + if in == nil { + return nil + } + out := new(ClusterClassUpgradeExternal) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterClassV1Beta1DeprecatedStatus) DeepCopyInto(out *ClusterClassV1Beta1DeprecatedStatus) { *out = *in @@ -803,6 +840,13 @@ func (in *ControlPlaneClassHealthCheckChecks) DeepCopyInto(out *ControlPlaneClas (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.UnhealthyMachineConditions != nil { + in, out := &in.UnhealthyMachineConditions, &out.UnhealthyMachineConditions + *out = make([]UnhealthyMachineCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneClassHealthCheckChecks. @@ -979,6 +1023,13 @@ func (in *ControlPlaneTopologyHealthCheckChecks) DeepCopyInto(out *ControlPlaneT (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.UnhealthyMachineConditions != nil { + in, out := &in.UnhealthyMachineConditions, &out.UnhealthyMachineConditions + *out = make([]UnhealthyMachineCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneTopologyHealthCheckChecks. @@ -1567,6 +1618,13 @@ func (in *MachineDeploymentClassHealthCheckChecks) DeepCopyInto(out *MachineDepl (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.UnhealthyMachineConditions != nil { + in, out := &in.UnhealthyMachineConditions, &out.UnhealthyMachineConditions + *out = make([]UnhealthyMachineCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineDeploymentClassHealthCheckChecks. @@ -2034,6 +2092,13 @@ func (in *MachineDeploymentTopologyHealthCheckChecks) DeepCopyInto(out *MachineD (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.UnhealthyMachineConditions != nil { + in, out := &in.UnhealthyMachineConditions, &out.UnhealthyMachineConditions + *out = make([]UnhealthyMachineCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineDeploymentTopologyHealthCheckChecks. @@ -2439,6 +2504,13 @@ func (in *MachineHealthCheckChecks) DeepCopyInto(out *MachineHealthCheckChecks) (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.UnhealthyMachineConditions != nil { + in, out := &in.UnhealthyMachineConditions, &out.UnhealthyMachineConditions + *out = make([]UnhealthyMachineCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineHealthCheckChecks. @@ -3369,6 +3441,11 @@ func (in *MachineSpec) DeepCopyInto(out *MachineSpec) { copy(*out, *in) } in.Deletion.DeepCopyInto(&out.Deletion) + if in.Taints != nil { + in, out := &in.Taints, &out.Taints + *out = make([]MachineTaint, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineSpec. @@ -3427,6 +3504,21 @@ func (in *MachineStatus) DeepCopy() *MachineStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineTaint) DeepCopyInto(out *MachineTaint) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineTaint. +func (in *MachineTaint) DeepCopy() *MachineTaint { + if in == nil { + return nil + } + out := new(MachineTaint) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineTemplateSpec) DeepCopyInto(out *MachineTemplateSpec) { *out = *in @@ -3664,6 +3756,26 @@ func (in *Topology) DeepCopy() *Topology { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UnhealthyMachineCondition) DeepCopyInto(out *UnhealthyMachineCondition) { + *out = *in + if in.TimeoutSeconds != nil { + in, out := &in.TimeoutSeconds, &out.TimeoutSeconds + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UnhealthyMachineCondition. +func (in *UnhealthyMachineCondition) DeepCopy() *UnhealthyMachineCondition { + if in == nil { + return nil + } + out := new(UnhealthyMachineCondition) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UnhealthyNodeCondition) DeepCopyInto(out *UnhealthyNodeCondition) { *out = *in diff --git a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.openapi.go b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.openapi.go index 6ada26c78c..6eecc0d326 100644 --- a/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.openapi.go +++ b/vendor/sigs.k8s.io/cluster-api/api/core/v1beta2/zz_generated.openapi.go @@ -42,6 +42,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassStatusVariable": schema_cluster_api_api_core_v1beta2_ClusterClassStatusVariable(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassStatusVariableDefinition": schema_cluster_api_api_core_v1beta2_ClusterClassStatusVariableDefinition(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassTemplateReference": schema_cluster_api_api_core_v1beta2_ClusterClassTemplateReference(ref), + "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgrade": schema_cluster_api_api_core_v1beta2_ClusterClassUpgrade(ref), + "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgradeExternal": schema_cluster_api_api_core_v1beta2_ClusterClassUpgradeExternal(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassV1Beta1DeprecatedStatus": schema_cluster_api_api_core_v1beta2_ClusterClassV1Beta1DeprecatedStatus(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassVariable": schema_cluster_api_api_core_v1beta2_ClusterClassVariable(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassVariableMetadata": schema_cluster_api_api_core_v1beta2_ClusterClassVariableMetadata(ref), @@ -161,6 +163,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineSetV1Beta1DeprecatedStatus": schema_cluster_api_api_core_v1beta2_MachineSetV1Beta1DeprecatedStatus(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineSpec": schema_cluster_api_api_core_v1beta2_MachineSpec(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineStatus": schema_cluster_api_api_core_v1beta2_MachineStatus(ref), + "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineTaint": schema_cluster_api_api_core_v1beta2_MachineTaint(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineTemplateSpec": schema_cluster_api_api_core_v1beta2_MachineTemplateSpec(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineV1Beta1DeprecatedStatus": schema_cluster_api_api_core_v1beta2_MachineV1Beta1DeprecatedStatus(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.NetworkRanges": schema_cluster_api_api_core_v1beta2_NetworkRanges(ref), @@ -171,6 +174,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api/api/core/v1beta2.PatchSelectorMatchMachineDeploymentClass": schema_cluster_api_api_core_v1beta2_PatchSelectorMatchMachineDeploymentClass(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.PatchSelectorMatchMachinePoolClass": schema_cluster_api_api_core_v1beta2_PatchSelectorMatchMachinePoolClass(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.Topology": schema_cluster_api_api_core_v1beta2_Topology(ref), + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition": schema_cluster_api_api_core_v1beta2_UnhealthyMachineCondition(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition": schema_cluster_api_api_core_v1beta2_UnhealthyNodeCondition(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ValidationRule": schema_cluster_api_api_core_v1beta2_ValidationRule(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.VariableSchema": schema_cluster_api_api_core_v1beta2_VariableSchema(ref), @@ -617,12 +621,39 @@ func schema_cluster_api_api_core_v1beta2_ClusterClassSpec(ref common.ReferenceCa }, }, }, + "upgrade": { + SchemaProps: spec.SchemaProps{ + Description: "upgrade defines the upgrade configuration for clusters using this ClusterClass.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgrade"), + }, + }, + "kubernetesVersions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "kubernetesVersions is the list of Kubernetes versions that can be used for clusters using this ClusterClass. The list of version must be ordered from the older to the newer version, and there should be at least one version for every minor in between the first and the last version.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, Required: []string{"infrastructure", "controlPlane"}, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterAvailabilityGate", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassPatch", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassVariable", "sigs.k8s.io/cluster-api/api/core/v1beta2.ControlPlaneClass", "sigs.k8s.io/cluster-api/api/core/v1beta2.InfrastructureClass", "sigs.k8s.io/cluster-api/api/core/v1beta2.WorkersClass"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterAvailabilityGate", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassPatch", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgrade", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassVariable", "sigs.k8s.io/cluster-api/api/core/v1beta2.ControlPlaneClass", "sigs.k8s.io/cluster-api/api/core/v1beta2.InfrastructureClass", "sigs.k8s.io/cluster-api/api/core/v1beta2.WorkersClass"}, } } @@ -823,6 +854,48 @@ func schema_cluster_api_api_core_v1beta2_ClusterClassTemplateReference(ref commo } } +func schema_cluster_api_api_core_v1beta2_ClusterClassUpgrade(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterClassUpgrade defines the upgrade configuration for clusters using the ClusterClass.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "external": { + SchemaProps: spec.SchemaProps{ + Description: "external defines external runtime extensions for upgrade operations.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgradeExternal"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgradeExternal"}, + } +} + +func schema_cluster_api_api_core_v1beta2_ClusterClassUpgradeExternal(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterClassUpgradeExternal defines external runtime extensions for upgrade operations.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "generateUpgradePlanExtension": { + SchemaProps: spec.SchemaProps{ + Description: "generateUpgradePlanExtension references an extension which is called to generate upgrade plan.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_cluster_api_api_core_v1beta2_ClusterClassV1Beta1DeprecatedStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1616,11 +1689,30 @@ func schema_cluster_api_api_core_v1beta2_ControlPlaneClassHealthCheckChecks(ref }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -1899,11 +1991,30 @@ func schema_cluster_api_api_core_v1beta2_ControlPlaneTopologyHealthCheckChecks(r }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -2920,11 +3031,30 @@ func schema_cluster_api_api_core_v1beta2_MachineDeploymentClassHealthCheckChecks }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -3700,11 +3830,30 @@ func schema_cluster_api_api_core_v1beta2_MachineDeploymentTopologyHealthCheckChe }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -4332,11 +4481,30 @@ func schema_cluster_api_api_core_v1beta2_MachineHealthCheckChecks(ref common.Ref }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -5996,12 +6164,35 @@ func schema_cluster_api_api_core_v1beta2_MachineSpec(ref common.ReferenceCallbac Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.MachineDeletionSpec"), }, }, + "taints": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "key", + "effect", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "taints are the node taints that Cluster API will manage. This list is not necessarily complete: other Kubernetes components may add or remove other taints from nodes, e.g. the node controller might add the node.kubernetes.io/not-ready taint. Only those taints defined in this list will be added or removed by core Cluster API controllers.\n\nThere can be at most 64 taints. A pod would have to tolerate all existing taints to run on the corresponding node.\n\nNOTE: This list is implemented as a \"map\" type, meaning that individual elements can be managed by different owners.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.MachineTaint"), + }, + }, + }, + }, + }, }, Required: []string{"clusterName", "bootstrap", "infrastructureRef"}, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.Bootstrap", "sigs.k8s.io/cluster-api/api/core/v1beta2.ContractVersionedObjectReference", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineDeletionSpec", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineReadinessGate"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.Bootstrap", "sigs.k8s.io/cluster-api/api/core/v1beta2.ContractVersionedObjectReference", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineDeletionSpec", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineReadinessGate", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineTaint"}, } } @@ -6022,7 +6213,7 @@ func schema_cluster_api_api_core_v1beta2_MachineStatus(ref common.ReferenceCallb }, }, SchemaProps: spec.SchemaProps{ - Description: "conditions represents the observations of a Machine's current state. Known condition types are Available, Ready, UpToDate, BootstrapConfigReady, InfrastructureReady, NodeReady, NodeHealthy, Deleting, Paused. If a MachineHealthCheck is targeting this machine, also HealthCheckSucceeded, OwnerRemediated conditions are added. Additionally control plane Machines controlled by KubeadmControlPlane will have following additional conditions: APIServerPodHealthy, ControllerManagerPodHealthy, SchedulerPodHealthy, EtcdPodHealthy, EtcdMemberHealthy.", + Description: "conditions represents the observations of a Machine's current state. Known condition types are Available, Ready, UpToDate, BootstrapConfigReady, InfrastructureReady, NodeReady, NodeHealthy, Updating, Deleting, Paused. If a MachineHealthCheck is targeting this machine, also HealthCheckSucceeded, OwnerRemediated conditions are added. Additionally control plane Machines controlled by KubeadmControlPlane will have following additional conditions: APIServerPodHealthy, ControllerManagerPodHealthy, SchedulerPodHealthy, EtcdPodHealthy, EtcdMemberHealthy.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -6114,6 +6305,48 @@ func schema_cluster_api_api_core_v1beta2_MachineStatus(ref common.ReferenceCallb } } +func schema_cluster_api_api_core_v1beta2_MachineTaint(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "MachineTaint defines a taint equivalent to corev1.Taint, but additionally having a propagation field.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "key is the taint key to be applied to a node. Must be a valid qualified name of maximum size 63 characters with an optional subdomain prefix of maximum size 253 characters, separated by a `/`.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "value is the taint value corresponding to the taint key. It must be a valid label value of maximum size 63 characters.", + Type: []string{"string"}, + Format: "", + }, + }, + "effect": { + SchemaProps: spec.SchemaProps{ + Description: "effect is the effect for the taint. Valid values are NoSchedule, PreferNoSchedule and NoExecute.", + Type: []string{"string"}, + Format: "", + }, + }, + "propagation": { + SchemaProps: spec.SchemaProps{ + Description: "propagation defines how this taint should be propagated to nodes. Valid values are 'Always' and 'OnInitialization'. Always: The taint will be continuously reconciled. If it is not set for a node, it will be added during reconciliation. OnInitialization: The taint will be added during node initialization. If it gets removed from the node later on it will not get added again.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"key", "effect", "propagation"}, + }, + }, + } +} + func schema_cluster_api_api_core_v1beta2_MachineTemplateSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -6518,6 +6751,41 @@ func schema_cluster_api_api_core_v1beta2_Topology(ref common.ReferenceCallback) } } +func schema_cluster_api_api_core_v1beta2_UnhealthyMachineCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UnhealthyMachineCondition represents a Machine condition type and value with a timeout specified as a duration. When the named condition has been in the given status for at least the timeout value, a machine is considered unhealthy.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "type of Machine condition", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "timeoutSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "timeoutSeconds is the duration that a machine must be in a given status for, after which the machine is considered unhealthy. For example, with a value of \"3600\", the machine must match the status for at least 1 hour before being considered unhealthy.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"type", "status", "timeoutSeconds"}, + }, + }, + } +} + func schema_cluster_api_api_core_v1beta2_UnhealthyNodeCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -6541,7 +6809,7 @@ func schema_cluster_api_api_core_v1beta2_UnhealthyNodeCondition(ref common.Refer }, "timeoutSeconds": { SchemaProps: spec.SchemaProps{ - Description: "timeoutSeconds is the duration that a node must be in a given status for, after which the node is considered unhealthy. For example, with a value of \"1h\", the node must match the status for at least 1 hour before being considered unhealthy.", + Description: "timeoutSeconds is the duration that a node must be in a given status for, after which the node is considered unhealthy. For example, with a value of \"3600\", the node must match the status for at least 1 hour before being considered unhealthy.", Type: []string{"integer"}, Format: "int32", }, diff --git a/vendor/sigs.k8s.io/cluster-api/feature/feature.go b/vendor/sigs.k8s.io/cluster-api/feature/feature.go index 15a6fb169b..da8dd8e8da 100644 --- a/vendor/sigs.k8s.io/cluster-api/feature/feature.go +++ b/vendor/sigs.k8s.io/cluster-api/feature/feature.go @@ -35,15 +35,6 @@ const ( // beta: v1.7 MachinePool featuregate.Feature = "MachinePool" - // ClusterResourceSet is a feature gate for the ClusterResourceSet functionality. - // - // alpha: v0.3 - // beta: v0.4 - // GA: v1.10 - // - // Deprecated: ClusterResourceSet feature is now GA and the corresponding feature flag will be removed in 1.12 release. - ClusterResourceSet featuregate.Feature = "ClusterResourceSet" - // ClusterTopology is a feature gate for the ClusterClass and managed topologies functionality. // // alpha: v0.4 @@ -77,6 +68,22 @@ const ( // // alpha: v1.10 PriorityQueue featuregate.Feature = "PriorityQueue" + + // ReconcilerRateLimiting is a feature gate that controls if reconcilers are rate-limited. + // Note: Currently the feature gate is rate-limiting to 1 request / 1 second. + // Note: If this feature gate is enabled the PriorityQueue feature gate must be enabled as well. + // + // alpha: v1.12 + ReconcilerRateLimiting featuregate.Feature = "ReconcilerRateLimiting" + + // InPlaceUpdates is a feature gate for the in-place machine updates functionality. + // alpha: v1.12 + InPlaceUpdates featuregate.Feature = "InPlaceUpdates" + + // MachineTaintPropagation is a feature gate for the machine taint propagation functionality. + // + // alpha: v1.12 + MachineTaintPropagation featuregate.Feature = "MachineTaintPropagation" ) func init() { @@ -87,12 +94,14 @@ func init() { // To add a new feature, define a key for it above and add it here. var defaultClusterAPIFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ // Every feature should be initiated here: - ClusterResourceSet: {Default: true, PreRelease: featuregate.GA}, MachinePool: {Default: true, PreRelease: featuregate.Beta}, MachineSetPreflightChecks: {Default: true, PreRelease: featuregate.Beta}, MachineWaitForVolumeDetachConsiderVolumeAttachments: {Default: true, PreRelease: featuregate.Beta}, PriorityQueue: {Default: false, PreRelease: featuregate.Alpha}, + ReconcilerRateLimiting: {Default: false, PreRelease: featuregate.Alpha}, ClusterTopology: {Default: false, PreRelease: featuregate.Alpha}, KubeadmBootstrapFormatIgnition: {Default: false, PreRelease: featuregate.Alpha}, RuntimeSDK: {Default: false, PreRelease: featuregate.Alpha}, + InPlaceUpdates: {Default: false, PreRelease: featuregate.Alpha}, + MachineTaintPropagation: {Default: false, PreRelease: featuregate.Alpha}, } diff --git a/vendor/sigs.k8s.io/cluster-api/util/certs/certs.go b/vendor/sigs.k8s.io/cluster-api/util/certs/certs.go index 225438bb4e..71c6e2436a 100644 --- a/vendor/sigs.k8s.io/cluster-api/util/certs/certs.go +++ b/vendor/sigs.k8s.io/cluster-api/util/certs/certs.go @@ -19,13 +19,18 @@ package certs import ( "crypto" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/pem" + "fmt" "github.com/pkg/errors" kerrors "k8s.io/apimachinery/pkg/util/errors" + + bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" ) // NewPrivateKey creates an RSA private key. @@ -113,3 +118,72 @@ func DecodePrivateKeyPEM(encoded []byte) (crypto.Signer, error) { return nil, kerrors.NewAggregate(errs) } + +// NewSigner creates a private key based on the provided encryption key algorithm. +func NewSigner(keyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType) (crypto.Signer, error) { + switch keyEncryptionAlgorithm { + case bootstrapv1.EncryptionAlgorithmECDSAP256: + return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case bootstrapv1.EncryptionAlgorithmECDSAP384: + return ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + } + rsaKeySize := rsaKeySizeFromAlgorithmType(keyEncryptionAlgorithm) + if rsaKeySize == 0 { + return nil, errors.Errorf("cannot obtain key size from unknown RSA algorithm: %q", keyEncryptionAlgorithm) + } + return rsa.GenerateKey(rand.Reader, rsaKeySize) +} + +// EncodePrivateKeyPEMFromSigner converts a known private key type of RSA or ECDSA to +// a PEM encoded block or returns an error. +func EncodePrivateKeyPEMFromSigner(key crypto.PrivateKey) ([]byte, error) { + switch t := key.(type) { + case *ecdsa.PrivateKey: + derBytes, err := x509.MarshalECPrivateKey(t) + if err != nil { + return nil, err + } + block := &pem.Block{ + Type: "EC PRIVATE KEY", + Bytes: derBytes, + } + return pem.EncodeToMemory(block), nil + case *rsa.PrivateKey: + block := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(t), + } + return pem.EncodeToMemory(block), nil + default: + return nil, fmt.Errorf("private key is not a recognized type: %T", key) + } +} + +// EncodePublicKeyPEMFromSigner returns PEM-encoded public key data. +func EncodePublicKeyPEMFromSigner(key crypto.PublicKey) ([]byte, error) { + der, err := x509.MarshalPKIXPublicKey(key) + if err != nil { + return []byte{}, err + } + block := pem.Block{ + Type: "PUBLIC KEY", + Bytes: der, + } + return pem.EncodeToMemory(&block), nil +} + +// rsaKeySizeFromAlgorithmType takes a known RSA algorithm defined in the kubeadm API and returns its key size. +// For unknown types it returns 0. +// For an empty type ("") which is the default (zero value) on the API field it returns the default size of 2048. +func rsaKeySizeFromAlgorithmType(keyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType) int { + switch keyEncryptionAlgorithm { + case bootstrapv1.EncryptionAlgorithmRSA2048, "": + return 2048 + case bootstrapv1.EncryptionAlgorithmRSA3072: + return 3072 + case bootstrapv1.EncryptionAlgorithmRSA4096: + return 4096 + default: + return 0 + } +} diff --git a/vendor/sigs.k8s.io/cluster-api/util/certs/types.go b/vendor/sigs.k8s.io/cluster-api/util/certs/types.go index 6039846872..462b781b7f 100644 --- a/vendor/sigs.k8s.io/cluster-api/util/certs/types.go +++ b/vendor/sigs.k8s.io/cluster-api/util/certs/types.go @@ -19,7 +19,6 @@ package certs import ( "crypto" "crypto/rand" - "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "math" @@ -49,7 +48,7 @@ type Config struct { } // NewSignedCert creates a signed certificate using the given CA certificate and key. -func (cfg *Config) NewSignedCert(key *rsa.PrivateKey, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) { +func (cfg *Config) NewSignedCert(key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) { serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) if err != nil { return nil, errors.Wrap(err, "failed to generate random integer for signed cerficate") diff --git a/vendor/sigs.k8s.io/cluster-api/util/conditions/sort.go b/vendor/sigs.k8s.io/cluster-api/util/conditions/sort.go index d0be13af9e..d06baeba82 100644 --- a/vendor/sigs.k8s.io/cluster-api/util/conditions/sort.go +++ b/vendor/sigs.k8s.io/cluster-api/util/conditions/sort.go @@ -76,6 +76,7 @@ func defaultSortLessFunc(i, j metav1.Condition) bool { // | OwnerRemediated | | | | | | x | // | -- Operations -- | | | | | | | // | TopologyReconciled | x | | | | | | +// | Updating | | | | | | x | // | RollingOut | x | x | x | | x | | // | Remediating | x | x | x | x | x | | // | ScalingDown | x | x | x | x | x | | @@ -117,6 +118,7 @@ var order = []string{ clusterv1.MachineHealthCheckSucceededCondition, clusterv1.MachineOwnerRemediatedCondition, clusterv1.ClusterTopologyReconciledCondition, + clusterv1.MachineUpdatingCondition, clusterv1.RollingOutCondition, clusterv1.RemediatingCondition, clusterv1.ScalingDownCondition, diff --git a/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/kubeconfig.go b/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/kubeconfig.go index b93c07d03a..a2087f6d1b 100644 --- a/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/kubeconfig.go +++ b/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/kubeconfig.go @@ -54,14 +54,17 @@ func FromSecret(ctx context.Context, c client.Reader, cluster client.ObjectKey) } // New creates a new Kubeconfig using the cluster name and specified endpoint. -func New(clusterName, endpoint string, caCert *x509.Certificate, caKey crypto.Signer) (*api.Config, error) { +func New(clusterName, endpoint string, caCert *x509.Certificate, caKey crypto.Signer, options ...KubeConfigOption) (*api.Config, error) { cfg := &certs.Config{ CommonName: "kubernetes-admin", Organization: []string{"system:masters"}, Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, } - clientKey, err := certs.NewPrivateKey() + kubeConfigOptions := &KubeConfigOptions{} + kubeConfigOptions.ApplyOptions(options) + + clientKey, err := certs.NewSigner(kubeConfigOptions.keyEncryptionAlgorithm) if err != nil { return nil, errors.Wrap(err, "unable to create private key") } @@ -71,6 +74,11 @@ func New(clusterName, endpoint string, caCert *x509.Certificate, caKey crypto.Si return nil, errors.Wrap(err, "unable to sign certificate") } + encodedClientKey, err := certs.EncodePrivateKeyPEMFromSigner(clientKey) + if err != nil { + return nil, errors.Wrap(err, "unable to encode private key") + } + userName := fmt.Sprintf("%s-admin", clusterName) contextName := fmt.Sprintf("%s@%s", userName, clusterName) @@ -89,7 +97,7 @@ func New(clusterName, endpoint string, caCert *x509.Certificate, caKey crypto.Si }, AuthInfos: map[string]*api.AuthInfo{ userName: { - ClientKeyData: certs.EncodePrivateKeyPEM(clientKey), + ClientKeyData: encodedClientKey, ClientCertificateData: certs.EncodeCertPEM(clientCert), }, }, @@ -98,23 +106,23 @@ func New(clusterName, endpoint string, caCert *x509.Certificate, caKey crypto.Si } // CreateSecret creates the Kubeconfig secret for the given cluster. -func CreateSecret(ctx context.Context, c client.Client, cluster *clusterv1.Cluster) error { +func CreateSecret(ctx context.Context, c client.Client, cluster *clusterv1.Cluster, options ...KubeConfigOption) error { name := util.ObjectKey(cluster) return CreateSecretWithOwner(ctx, c, name, cluster.Spec.ControlPlaneEndpoint.String(), metav1.OwnerReference{ APIVersion: clusterv1.GroupVersion.String(), Kind: "Cluster", Name: cluster.Name, UID: cluster.UID, - }) + }, options...) } // CreateSecretWithOwner creates the Kubeconfig secret for the given cluster name, namespace, endpoint, and owner reference. -func CreateSecretWithOwner(ctx context.Context, c client.Client, clusterName client.ObjectKey, endpoint string, owner metav1.OwnerReference) error { +func CreateSecretWithOwner(ctx context.Context, c client.Client, clusterName client.ObjectKey, endpoint string, owner metav1.OwnerReference, options ...KubeConfigOption) error { server, err := url.JoinPath("https://", endpoint) if err != nil { return err } - out, err := generateKubeconfig(ctx, c, clusterName, server) + out, err := generateKubeconfig(ctx, c, clusterName, server, options...) if err != nil { return err } @@ -181,7 +189,7 @@ func NeedsClientCertRotation(configSecret *corev1.Secret, threshold time.Duratio } // RegenerateSecret creates and stores a new Kubeconfig in the given secret. -func RegenerateSecret(ctx context.Context, c client.Client, configSecret *corev1.Secret) error { +func RegenerateSecret(ctx context.Context, c client.Client, configSecret *corev1.Secret, options ...KubeConfigOption) error { clusterName, _, err := secret.ParseSecretName(configSecret.Name) if err != nil { return errors.Wrap(err, "failed to parse secret name") @@ -197,7 +205,7 @@ func RegenerateSecret(ctx context.Context, c client.Client, configSecret *corev1 } endpoint := config.Clusters[clusterName].Server key := client.ObjectKey{Name: clusterName, Namespace: configSecret.Namespace} - out, err := generateKubeconfig(ctx, c, key, endpoint) + out, err := generateKubeconfig(ctx, c, key, endpoint, options...) if err != nil { return err } @@ -205,7 +213,7 @@ func RegenerateSecret(ctx context.Context, c client.Client, configSecret *corev1 return c.Update(ctx, configSecret) } -func generateKubeconfig(ctx context.Context, c client.Client, clusterName client.ObjectKey, endpoint string) ([]byte, error) { +func generateKubeconfig(ctx context.Context, c client.Client, clusterName client.ObjectKey, endpoint string, options ...KubeConfigOption) ([]byte, error) { clusterCA, err := secret.GetFromNamespacedName(ctx, c, clusterName, secret.ClusterCA) if err != nil { if apierrors.IsNotFound(err) { @@ -228,7 +236,7 @@ func generateKubeconfig(ctx context.Context, c client.Client, clusterName client return nil, errors.New("CA private key not found") } - cfg, err := New(clusterName.Name, endpoint, cert, key) + cfg, err := New(clusterName.Name, endpoint, cert, key, options...) if err != nil { return nil, errors.Wrap(err, "failed to generate a kubeconfig") } diff --git a/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/options.go b/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/options.go new file mode 100644 index 0000000000..bbeaee3a24 --- /dev/null +++ b/vendor/sigs.k8s.io/cluster-api/util/kubeconfig/options.go @@ -0,0 +1,46 @@ +/* +Copyright 2019 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 kubeconfig + +import bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" + +// KubeConfigOption helps to modify KubeConfigOptions. +type KubeConfigOption interface { //nolint:revive + // ApplyKubeConfigOption applies this options to the given KubeConfigOptions options. + ApplyKubeConfigOption(*KubeConfigOptions) +} + +// KubeConfigOptions allows to set options for generating a kubeconfig. +type KubeConfigOptions struct { //nolint:revive + keyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType +} + +// ApplyOptions applies the given list options on these options, +// and then returns itself (for convenient chaining). +func (o *KubeConfigOptions) ApplyOptions(opts []KubeConfigOption) { + for _, opt := range opts { + opt.ApplyKubeConfigOption(o) + } +} + +// KeyEncryptionAlgorithm allows to specify the key encryption algorithm type. +type KeyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType + +// ApplyKubeConfigOption applies this configuration to the given kube configuration options. +func (t KeyEncryptionAlgorithm) ApplyKubeConfigOption(opts *KubeConfigOptions) { + opts.keyEncryptionAlgorithm = bootstrapv1.EncryptionAlgorithmType(t) +} diff --git a/vendor/sigs.k8s.io/cluster-api/util/patch/patch.go b/vendor/sigs.k8s.io/cluster-api/util/patch/patch.go index 1c994e3b88..64378f457e 100644 --- a/vendor/sigs.k8s.io/cluster-api/util/patch/patch.go +++ b/vendor/sigs.k8s.io/cluster-api/util/patch/patch.go @@ -143,18 +143,16 @@ func (h *Helper) Patch(ctx context.Context, obj client.Object, opts ...Option) e return errors.Wrapf(err, "failed to patch %s %s: failed to convert after object to Unstructured", h.gvk.Kind, klog.KObj(h.beforeObject)) } - // Determine if the object has status. - if unstructuredHasStatus(h.after) { - if options.IncludeStatusObservedGeneration { - // Set status.observedGeneration if we're asked to do so. - if err := unstructured.SetNestedField(h.after.Object, h.after.GetGeneration(), "status", "observedGeneration"); err != nil { - return errors.Wrapf(err, "failed to patch %s %s: failed to set .status.observedGeneration", h.gvk.Kind, klog.KObj(h.beforeObject)) - } + // Include .status.observedGeneration if IncludeStatusObservedGeneration is set. + if options.IncludeStatusObservedGeneration { + // Set status.observedGeneration if we're asked to do so. + if err := unstructured.SetNestedField(h.after.Object, h.after.GetGeneration(), "status", "observedGeneration"); err != nil { + return errors.Wrapf(err, "failed to patch %s %s: failed to set .status.observedGeneration", h.gvk.Kind, klog.KObj(h.beforeObject)) + } - // Restore the changes back to the original object. - if err := runtime.DefaultUnstructuredConverter.FromUnstructured(h.after.Object, obj); err != nil { - return errors.Wrapf(err, "failed to patch %s %s: failed to converted object from Unstructured", h.gvk.Kind, klog.KObj(h.beforeObject)) - } + // Restore the changes back to the original object. + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(h.after.Object, obj); err != nil { + return errors.Wrapf(err, "failed to patch %s %s: failed to converted object from Unstructured", h.gvk.Kind, klog.KObj(h.beforeObject)) } } @@ -172,17 +170,17 @@ func (h *Helper) Patch(ctx context.Context, obj client.Object, opts ...Option) e // patching conditions first avoids an extra loop if spec or status patch succeeds first // given that causes the resourceVersion to mutate. if err := h.patchStatusConditions(ctx, obj, options.ForceOverwriteConditions, options.OwnedConditions, options.OwnedV1Beta2Conditions); err != nil { - errs = append(errs, err) + errs = append(errs, errors.Wrapf(err, "failed to patch status conditions")) } // Then proceed to patch the rest of the object. if err := h.patch(ctx, obj); err != nil { - errs = append(errs, err) + errs = append(errs, errors.Wrapf(err, "failed to patch spec and metadata")) } if err := h.patchStatus(ctx, obj); err != nil { //nolint:staticcheck if !(apierrors.IsNotFound(err) && !obj.GetDeletionTimestamp().IsZero() && len(obj.GetFinalizers()) == 0) { - errs = append(errs, err) + errs = append(errs, errors.Wrapf(err, "failed to patch status")) } } diff --git a/vendor/sigs.k8s.io/cluster-api/util/patch/utils.go b/vendor/sigs.k8s.io/cluster-api/util/patch/utils.go index c24ca7dddb..94d6fbf2a7 100644 --- a/vendor/sigs.k8s.io/cluster-api/util/patch/utils.go +++ b/vendor/sigs.k8s.io/cluster-api/util/patch/utils.go @@ -43,11 +43,6 @@ var ( } ) -func unstructuredHasStatus(u *unstructured.Unstructured) bool { - _, ok := u.Object["status"] - return ok -} - // toUnstructured converts an object to Unstructured. // We have to pass in a gvk as we can't rely on GVK being set in a runtime.Object. func toUnstructured(obj runtime.Object, gvk schema.GroupVersionKind) (*unstructured.Unstructured, error) { diff --git a/vendor/sigs.k8s.io/cluster-api/util/secret/certificates.go b/vendor/sigs.k8s.io/cluster-api/util/secret/certificates.go index 8af1c12eec..b8dc6405fe 100644 --- a/vendor/sigs.k8s.io/cluster-api/util/secret/certificates.go +++ b/vendor/sigs.k8s.io/cluster-api/util/secret/certificates.go @@ -18,8 +18,8 @@ package secret import ( "context" + "crypto" "crypto/rand" - "crypto/rsa" "crypto/sha256" "crypto/x509" "crypto/x509/pkix" @@ -69,6 +69,7 @@ type Certificates []*Certificate // NewCertificatesForInitialControlPlane returns a list of certificates configured for a control plane node. func NewCertificatesForInitialControlPlane(config *bootstrapv1.ClusterConfiguration) Certificates { var validityPeriodDays int32 + var keyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType certificatesDir := DefaultCertificatesDir if config != nil { if config.CertificatesDir != "" { @@ -77,34 +78,41 @@ func NewCertificatesForInitialControlPlane(config *bootstrapv1.ClusterConfigurat if config.CACertificateValidityPeriodDays != 0 { validityPeriodDays = config.CACertificateValidityPeriodDays } + if config.EncryptionAlgorithm != "" { + keyEncryptionAlgorithm = config.EncryptionAlgorithm + } } certificates := Certificates{ &Certificate{ - Purpose: ClusterCA, - CertFile: path.Join(certificatesDir, "ca.crt"), - KeyFile: path.Join(certificatesDir, "ca.key"), - ValidityPeriodDays: validityPeriodDays, + Purpose: ClusterCA, + CertFile: path.Join(certificatesDir, "ca.crt"), + KeyFile: path.Join(certificatesDir, "ca.key"), + ValidityPeriodDays: validityPeriodDays, + KeyEncryptionAlgorithm: keyEncryptionAlgorithm, }, &Certificate{ - Purpose: ServiceAccount, - CertFile: path.Join(certificatesDir, "sa.pub"), - KeyFile: path.Join(certificatesDir, "sa.key"), - ValidityPeriodDays: validityPeriodDays, + Purpose: ServiceAccount, + CertFile: path.Join(certificatesDir, "sa.pub"), + KeyFile: path.Join(certificatesDir, "sa.key"), + ValidityPeriodDays: validityPeriodDays, + KeyEncryptionAlgorithm: keyEncryptionAlgorithm, }, &Certificate{ - Purpose: FrontProxyCA, - CertFile: path.Join(certificatesDir, "front-proxy-ca.crt"), - KeyFile: path.Join(certificatesDir, "front-proxy-ca.key"), - ValidityPeriodDays: validityPeriodDays, + Purpose: FrontProxyCA, + CertFile: path.Join(certificatesDir, "front-proxy-ca.crt"), + KeyFile: path.Join(certificatesDir, "front-proxy-ca.key"), + ValidityPeriodDays: validityPeriodDays, + KeyEncryptionAlgorithm: keyEncryptionAlgorithm, }, } etcdCert := &Certificate{ - Purpose: EtcdCA, - CertFile: path.Join(certificatesDir, "etcd", "ca.crt"), - KeyFile: path.Join(certificatesDir, "etcd", "ca.key"), - ValidityPeriodDays: validityPeriodDays, + Purpose: EtcdCA, + CertFile: path.Join(certificatesDir, "etcd", "ca.crt"), + KeyFile: path.Join(certificatesDir, "etcd", "ca.key"), + ValidityPeriodDays: validityPeriodDays, + KeyEncryptionAlgorithm: keyEncryptionAlgorithm, } // TODO make sure all the fields are actually defined and return an error if not @@ -114,13 +122,13 @@ func NewCertificatesForInitialControlPlane(config *bootstrapv1.ClusterConfigurat CertFile: config.Etcd.External.CAFile, External: true, } - apiserverEtcdClientCert := &Certificate{ + apiServerEtcdClientCert := &Certificate{ Purpose: APIServerEtcdClient, CertFile: config.Etcd.External.CertFile, KeyFile: config.Etcd.External.KeyFile, External: true, } - certificates = append(certificates, apiserverEtcdClientCert) + certificates = append(certificates, apiServerEtcdClientCert) } certificates = append(certificates, etcdCert) @@ -331,13 +339,14 @@ func (c Certificates) LookupOrGenerateCached(ctx context.Context, secretCachingC // Certificate represents a single certificate CA. type Certificate struct { - Generated bool - External bool - Purpose Purpose - KeyPair *certs.KeyPair - CertFile, KeyFile string - Secret *corev1.Secret - ValidityPeriodDays int32 + Generated bool + External bool + Purpose Purpose + KeyPair *certs.KeyPair + CertFile, KeyFile string + Secret *corev1.Secret + ValidityPeriodDays int32 + KeyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType } // Hashes hashes all the certificates stored in a CA certificate. @@ -420,7 +429,7 @@ func (c *Certificate) Generate() error { generator = generateServiceAccountKeys } - kp, err := generator(c.ValidityPeriodDays) + kp, err := generator(c.ValidityPeriodDays, c.KeyEncryptionAlgorithm) if err != nil { return err } @@ -473,35 +482,44 @@ func secretToKeyPair(s *corev1.Secret) (*certs.KeyPair, error) { }, nil } -func generateCACert(validityPeriodDays int32) (*certs.KeyPair, error) { - x509Cert, privKey, err := newCertificateAuthority(validityPeriodDays) +func generateCACert(validityPeriodDays int32, keyAlgorithmType bootstrapv1.EncryptionAlgorithmType) (*certs.KeyPair, error) { + x509Cert, privateKey, err := newCertificateAuthority(validityPeriodDays, keyAlgorithmType) + if err != nil { + return nil, err + } + encodedKey, err := certs.EncodePrivateKeyPEMFromSigner(privateKey) if err != nil { return nil, err } return &certs.KeyPair{ Cert: certs.EncodeCertPEM(x509Cert), - Key: certs.EncodePrivateKeyPEM(privKey), + Key: encodedKey, }, nil } -func generateServiceAccountKeys(_ int32) (*certs.KeyPair, error) { - saCreds, err := certs.NewPrivateKey() +func generateServiceAccountKeys(_ int32, keyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType) (*certs.KeyPair, error) { + saCreds, err := certs.NewSigner(keyEncryptionAlgorithm) if err != nil { return nil, err } - saPub, err := certs.EncodePublicKeyPEM(&saCreds.PublicKey) + saPub, err := certs.EncodePublicKeyPEMFromSigner(saCreds.Public()) if err != nil { return nil, err } + saKey, err := certs.EncodePrivateKeyPEMFromSigner(saCreds) + if err != nil { + return nil, err + } + return &certs.KeyPair{ Cert: saPub, - Key: certs.EncodePrivateKeyPEM(saCreds), + Key: saKey, }, nil } // newCertificateAuthority creates new certificate and private key for the certificate authority. -func newCertificateAuthority(validityPeriodDays int32) (*x509.Certificate, *rsa.PrivateKey, error) { - key, err := certs.NewPrivateKey() +func newCertificateAuthority(validityPeriodDays int32, keyAlgorithmType bootstrapv1.EncryptionAlgorithmType) (*x509.Certificate, crypto.Signer, error) { + key, err := certs.NewSigner(keyAlgorithmType) if err != nil { return nil, nil, err } @@ -515,7 +533,7 @@ func newCertificateAuthority(validityPeriodDays int32) (*x509.Certificate, *rsa. } // newSelfSignedCACert creates a CA certificate. -func newSelfSignedCACert(key *rsa.PrivateKey, validityPeriodDays int32) (*x509.Certificate, error) { +func newSelfSignedCACert(key crypto.Signer, validityPeriodDays int32) (*x509.Certificate, error) { cfg := certs.Config{ CommonName: "kubernetes", } diff --git a/vendor/sigs.k8s.io/cluster-api/util/util.go b/vendor/sigs.k8s.io/cluster-api/util/util.go index 684ef3d07b..992d574dde 100644 --- a/vendor/sigs.k8s.io/cluster-api/util/util.go +++ b/vendor/sigs.k8s.io/cluster-api/util/util.go @@ -37,6 +37,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -179,7 +180,7 @@ func GetClusterByName(ctx context.Context, c client.Client, namespace, name stri } if err := c.Get(ctx, key, cluster); err != nil { - return nil, errors.Wrapf(err, "failed to get Cluster/%s", name) + return nil, errors.Wrapf(err, "failed to get Cluster %s", klog.KRef(namespace, name)) } return cluster, nil @@ -359,9 +360,9 @@ func indexOwnerRef(ownerReferences []metav1.OwnerReference, ref metav1.OwnerRefe // IsOwnedByObject returns true if any of the owner references point to the given target. // It matches the object based on the Group, Kind and Name. -func IsOwnedByObject(obj metav1.Object, target client.Object) bool { +func IsOwnedByObject(obj metav1.Object, target client.Object, targetGK schema.GroupKind) bool { for _, ref := range obj.GetOwnerReferences() { - if refersTo(&ref, target) { + if refersTo(&ref, target, targetGK) { return true } } @@ -369,12 +370,12 @@ func IsOwnedByObject(obj metav1.Object, target client.Object) bool { } // IsControlledBy differs from metav1.IsControlledBy. This function matches on Group, Kind and Name. The metav1.IsControlledBy function matches on UID only. -func IsControlledBy(obj metav1.Object, owner client.Object) bool { +func IsControlledBy(obj metav1.Object, owner client.Object, ownerGK schema.GroupKind) bool { controllerRef := metav1.GetControllerOfNoCopy(obj) if controllerRef == nil { return false } - return refersTo(controllerRef, owner) + return refersTo(controllerRef, owner, ownerGK) } // Returns true if a and b point to the same object based on Group, Kind and Name. @@ -393,14 +394,13 @@ func referSameObject(a, b metav1.OwnerReference) bool { } // Returns true if ref refers to obj based on Group, Kind and Name. -func refersTo(ref *metav1.OwnerReference, obj client.Object) bool { +func refersTo(ref *metav1.OwnerReference, obj client.Object, objGK schema.GroupKind) bool { refGv, err := schema.ParseGroupVersion(ref.APIVersion) if err != nil { return false } - gvk := obj.GetObjectKind().GroupVersionKind() - return refGv.Group == gvk.Group && ref.Kind == gvk.Kind && ref.Name == obj.GetName() + return refGv.Group == objGK.Group && ref.Kind == objGK.Kind && ref.Name == obj.GetName() } // UnstructuredUnmarshalField is a wrapper around json and unstructured objects to decode and copy a specific field @@ -690,6 +690,8 @@ func IsSupportedVersionSkew(a, b semver.Version) bool { // LowestNonZeroResult compares two reconciliation results // and returns the one with lowest requeue time. +// +//nolint:staticcheck // SA1019: Requeue is deprecated. func LowestNonZeroResult(i, j ctrl.Result) ctrl.Result { switch { case i.IsZero(): @@ -751,3 +753,89 @@ func MergeMap(maps ...map[string]string) map[string]string { } return m } + +// GetOwnerMachinePool returns the MachinePool objects owning the current resource. +func GetOwnerMachinePool(ctx context.Context, c client.Client, obj metav1.ObjectMeta) (*clusterv1.MachinePool, error) { + for _, ref := range obj.GetOwnerReferences() { + if ref.Kind != "MachinePool" { + continue + } + gv, err := schema.ParseGroupVersion(ref.APIVersion) + if err != nil { + return nil, errors.WithStack(err) + } + if gv.Group == clusterv1.GroupVersion.Group { + return GetMachinePoolByName(ctx, c, obj.Namespace, ref.Name) + } + } + return nil, nil +} + +// GetMachinePoolByName finds and returns a MachinePool object using the specified params. +func GetMachinePoolByName(ctx context.Context, c client.Client, namespace, name string) (*clusterv1.MachinePool, error) { + m := &clusterv1.MachinePool{} + key := client.ObjectKey{Name: name, Namespace: namespace} + if err := c.Get(ctx, key, m); err != nil { + return nil, err + } + return m, nil +} + +// GetMachinePoolByLabels finds and returns a MachinePool object using the value of clusterv1.MachinePoolNameLabel. +// This differs from GetMachinePoolByName as the label value can be a hash. +func GetMachinePoolByLabels(ctx context.Context, c client.Client, namespace string, labels map[string]string) (*clusterv1.MachinePool, error) { + selector := map[string]string{} + if clusterName, ok := labels[clusterv1.ClusterNameLabel]; ok { + selector = map[string]string{clusterv1.ClusterNameLabel: clusterName} + } + + if poolNameHash, ok := labels[clusterv1.MachinePoolNameLabel]; ok { + machinePoolList := &clusterv1.MachinePoolList{} + if err := c.List(ctx, machinePoolList, client.InNamespace(namespace), client.MatchingLabels(selector)); err != nil { + return nil, errors.Wrapf(err, "failed to list MachinePools using labels %v", selector) + } + + for _, mp := range machinePoolList.Items { + if format.MustFormatValue(mp.Name) == poolNameHash { + return &mp, nil + } + } + } else { + return nil, errors.Errorf("labels missing required key `%s`", clusterv1.MachinePoolNameLabel) + } + + return nil, nil +} + +// MachinePoolToInfrastructureMapFunc returns a handler.MapFunc that watches for +// MachinePool events and returns reconciliation requests for an infrastructure provider object. +func MachinePoolToInfrastructureMapFunc(ctx context.Context, gvk schema.GroupVersionKind) handler.MapFunc { + log := ctrl.LoggerFrom(ctx) + return func(_ context.Context, o client.Object) []reconcile.Request { + m, ok := o.(*clusterv1.MachinePool) + if !ok { + log.V(4).Info("Not a machine pool", "Object", klog.KObj(o)) + return nil + } + log := log.WithValues("MachinePool", klog.KObj(o)) + + gk := gvk.GroupKind() + ref := m.Spec.Template.Spec.InfrastructureRef + // Return early if the GroupKind doesn't match what we expect. + infraGK := ref.GroupKind() + if gk != infraGK { + log.V(4).Info("Infra kind doesn't match filter group kind", "infrastructureGroupKind", infraGK.String()) + return nil + } + + log.V(4).Info("Projecting object") + return []reconcile.Request{ + { + NamespacedName: client.ObjectKey{ + Namespace: m.Namespace, + Name: ref.Name, + }, + }, + } + } +}